Skip to main content

Module 0x2::package

Functions for operating on Move packages from within Move:

use 0x1::ascii;
use 0x1::type_name;
use 0x2::object;
use 0x2::transfer;
use 0x2::tx_context;
use 0x2::types;

Resource Publisher

This type can only be created in the transaction that generates a module, by consuming its one-time witness, so it can be used to identify the address that published the package a type originated from.

struct Publisher has store, key
Fields

Resource UpgradeCap

Capability controlling the ability to upgrade a package.

struct UpgradeCap has store, key
Fields
id: object::UID
package: object::ID
(Mutable) ID of the package that can be upgraded.
version: u64
(Mutable) The number of upgrades that have been applied successively to the original package. Initially 0.
policy: u8
What kind of upgrades are allowed.

Struct UpgradeTicket

Permission to perform a particular upgrade (for a fixed version of the package, bytecode to upgrade with and transitive dependencies to depend against).

An UpgradeCap can only issue one ticket at a time, to prevent races between concurrent updates or a change in its upgrade policy after issuing a ticket, so the ticket is a "Hot Potato" to preserve forward progress.

struct UpgradeTicket
Fields
cap: object::ID
(Immutable) ID of the UpgradeCap this originated from.
package: object::ID
(Immutable) ID of the package that can be upgraded.
policy: u8
(Immutable) The policy regarding what kind of upgrade this ticket permits.
digest: vector<u8>
(Immutable) SHA256 digest of the bytecode and transitive dependencies that will be used in the upgrade.

Struct UpgradeReceipt

Issued as a result of a successful upgrade, containing the information to be used to update the UpgradeCap. This is a "Hot Potato" to ensure that it is used to update its UpgradeCap before the end of the transaction that performed the upgrade.

struct UpgradeReceipt
Fields
cap: object::ID
(Immutable) ID of the UpgradeCap this originated from.
package: object::ID
(Immutable) ID of the package after it was upgraded.

Constants

Add new functions or types, or change dependencies, existing functions can't change.

const ADDITIVE: u8 = 128;

Update any part of the package (function implementations, add new functions or types, change dependencies)

const COMPATIBLE: u8 = 0;

Only be able to change dependencies.

const DEP_ONLY: u8 = 192;

This UpgradeCap has already authorized a pending upgrade.

const EAlreadyAuthorized: u64 = 2;

This UpgradeCap has not authorized an upgrade.

const ENotAuthorized: u64 = 3;

Tried to create a Publisher using a type that isn't a one-time witness.

const ENotOneTimeWitness: u64 = 0;

Tried to set a less restrictive policy than currently in place.

const ETooPermissive: u64 = 1;

Trying to commit an upgrade to the wrong UpgradeCap.

const EWrongUpgradeCap: u64 = 4;

Function claim

Claim a Publisher object. Requires a One-Time-Witness to prove ownership. Due to this constraint there can be only one Publisher object per module but multiple per package (!).

public fun claim<OTW: drop>(otw: OTW, ctx: &mut tx_context::TxContext): package::Publisher
Implementation
public fun claim<OTW: drop>(otw: OTW, ctx: &mut TxContext): Publisher {
    assert!(types::is_one_time_witness(&otw), ENotOneTimeWitness);

    let tyname = type_name::get_with_original_ids<OTW>();

    Publisher {
        id: object::new(ctx),
        package: tyname.get_address(),
        module_name: tyname.get_module(),
    }
}

Function claim_and_keep

Claim a Publisher object and send it to transaction sender. Since this function can only be called in the module initializer, the sender is the publisher.

public fun claim_and_keep<OTW: drop>(otw: OTW, ctx: &mut tx_context::TxContext)
Implementation
public fun claim_and_keep<OTW: drop>(otw: OTW, ctx: &mut TxContext) {
    sui::transfer::public_transfer(claim(otw, ctx), ctx.sender())
}

Function burn_publisher

Destroy a Publisher object effectively removing all privileges associated with it.

public fun burn_publisher(self: package::Publisher)
Implementation
public fun burn_publisher(self: Publisher) {
    let Publisher { id, package: _, module_name: _ } = self;
    id.delete();
}

Function from_package

Check whether type belongs to the same package as the publisher object.

public fun from_package<T>(self: &package::Publisher): bool
Implementation
public fun from_package<T>(self: &Publisher): bool {
    type_name::get_with_original_ids<T>().get_address() == self.package
}

Function from_module

Check whether a type belongs to the same module as the publisher object.

public fun from_module<T>(self: &package::Publisher): bool
Implementation
public fun from_module<T>(self: &Publisher): bool {
    let tyname = type_name::get_with_original_ids<T>();

    (tyname.get_address() == self.package) && (tyname.get_module() == self.module_name)
}

Function published_module

Read the name of the module.

public fun published_module(self: &package::Publisher): &ascii::String
Implementation
public fun published_module(self: &Publisher): &String {
    &self.module_name
}

Function published_package

Read the package address string.

public fun published_package(self: &package::Publisher): &ascii::String
Implementation
public fun published_package(self: &Publisher): &String {
    &self.package
}

Function upgrade_package

The ID of the package that this cap authorizes upgrades for. Can be 0x0 if the cap cannot currently authorize an upgrade because there is already a pending upgrade in the transaction. Otherwise guaranteed to be the latest version of any given package.

public fun upgrade_package(cap: &package::UpgradeCap): object::ID
Implementation
public fun upgrade_package(cap: &UpgradeCap): ID {
    cap.package
}

Function version

The most recent version of the package, increments by one for each successfully applied upgrade.

public fun version(cap: &package::UpgradeCap): u64
Implementation
public fun version(cap: &UpgradeCap): u64 {
    cap.version
}

Function upgrade_policy

The most permissive kind of upgrade currently supported by this cap.

public fun upgrade_policy(cap: &package::UpgradeCap): u8
Implementation
public fun upgrade_policy(cap: &UpgradeCap): u8 {
    cap.policy
}

Function ticket_package

The package that this ticket is authorized to upgrade

public fun ticket_package(ticket: &package::UpgradeTicket): object::ID
Implementation
public fun ticket_package(ticket: &UpgradeTicket): ID {
    ticket.package
}

Function ticket_policy

The kind of upgrade that this ticket authorizes.

public fun ticket_policy(ticket: &package::UpgradeTicket): u8
Implementation
public fun ticket_policy(ticket: &UpgradeTicket): u8 {
    ticket.policy
}

Function receipt_cap

ID of the UpgradeCap that this receipt should be used to update.

public fun receipt_cap(receipt: &package::UpgradeReceipt): object::ID
Implementation
public fun receipt_cap(receipt: &UpgradeReceipt): ID {
    receipt.cap
}

Function receipt_package

ID of the package that was upgraded to: the latest version of the package, as of the upgrade represented by this receipt.

public fun receipt_package(receipt: &package::UpgradeReceipt): object::ID
Implementation
public fun receipt_package(receipt: &UpgradeReceipt): ID {
    receipt.package
}

Function ticket_digest

A hash of the package contents for the new version of the package. This ticket only authorizes an upgrade to a package that matches this digest. A package's contents are identified by two things:

  • modules: [[u8]] a list of the package's module contents
  • deps: [[u8; 32]] a list of 32 byte ObjectIDs of the package's transitive dependencies

A package's digest is calculated as:

sha3_256(sort(modules ++ deps))

public fun ticket_digest(ticket: &package::UpgradeTicket): &vector<u8>
Implementation
public fun ticket_digest(ticket: &UpgradeTicket): &vector<u8> {
    &ticket.digest
}

Function compatible_policy

Expose the constants representing various upgrade policies

public fun compatible_policy(): u8
Implementation
public fun compatible_policy(): u8 { COMPATIBLE }

Function additive_policy

public fun additive_policy(): u8
Implementation
public fun additive_policy(): u8 { ADDITIVE }

Function dep_only_policy

public fun dep_only_policy(): u8
Implementation
public fun dep_only_policy(): u8 { DEP_ONLY }

Function only_additive_upgrades

Restrict upgrades through this upgrade cap to just add code, or change dependencies.

public entry fun only_additive_upgrades(cap: &mut package::UpgradeCap)
Implementation
public entry fun only_additive_upgrades(cap: &mut UpgradeCap) {
    cap.restrict(ADDITIVE)
}

Function only_dep_upgrades

Restrict upgrades through this upgrade cap to just change dependencies.

public entry fun only_dep_upgrades(cap: &mut package::UpgradeCap)
Implementation
public entry fun only_dep_upgrades(cap: &mut UpgradeCap) {
    cap.restrict(DEP_ONLY)
}

Function make_immutable

Discard the UpgradeCap to make a package immutable.

public entry fun make_immutable(cap: package::UpgradeCap)
Implementation
public entry fun make_immutable(cap: UpgradeCap) {
    let UpgradeCap { id, package: _, version: _, policy: _ } = cap;
    id.delete();
}

Function authorize_upgrade

Issue a ticket authorizing an upgrade to a particular new bytecode (identified by its digest). A ticket will only be issued if one has not already been issued, and if the policy requested is at least as restrictive as the policy set out by the cap.

The digest supplied and the policy will both be checked by validators when running the upgrade. I.e. the bytecode supplied in the upgrade must have a matching digest, and the changes relative to the parent package must be compatible with the policy in the ticket for the upgrade to succeed.

public fun authorize_upgrade(cap: &mut package::UpgradeCap, policy: u8, digest: vector<u8>): package::UpgradeTicket
Implementation
public fun authorize_upgrade(
    cap: &mut UpgradeCap,
    policy: u8,
    digest: vector<u8>
): UpgradeTicket {
    let id_zero = @0x0.to_id();
    assert!(cap.package != id_zero, EAlreadyAuthorized);
    assert!(policy >= cap.policy, ETooPermissive);

    let package = cap.package;
    cap.package = id_zero;

    UpgradeTicket {
        cap: object::id(cap),
        package,
        policy,
        digest,
    }
}

Function commit_upgrade

Consume an UpgradeReceipt to update its UpgradeCap, finalizing the upgrade.

public fun commit_upgrade(cap: &mut package::UpgradeCap, receipt: package::UpgradeReceipt)
Implementation
public fun commit_upgrade(
    cap: &mut UpgradeCap,
    receipt: UpgradeReceipt,
) {
    let UpgradeReceipt { cap: cap_id, package } = receipt;

    assert!(object::id(cap) == cap_id, EWrongUpgradeCap);
    assert!(cap.package.to_address() == @0x0, ENotAuthorized);

    cap.package = package;
    cap.version = cap.version + 1;
}

Function restrict

fun restrict(cap: &mut package::UpgradeCap, policy: u8)
Implementation
fun restrict(cap: &mut UpgradeCap, policy: u8) {
    assert!(cap.policy <= policy, ETooPermissive);
    cap.policy = policy;
}