Skip to main content

Module 0x2::versioned

use 0x2::dynamic_field;
use 0x2::object;
use 0x2::tx_context;

Resource Versioned

A wrapper type that supports versioning of the inner type. The inner type is a dynamic field of the Versioned object, and is keyed using version. User of this type could load the inner object using corresponding type based on the version. You can also upgrade the inner object to a new type version. If you want to support lazy upgrade of the inner type, one caveat is that all APIs would have to use mutable reference even if it's a read-only API.

struct Versioned has store, key
Fields
id: object::UID
version: u64

Struct VersionChangeCap

Represents a hot potato object generated when we take out the dynamic field. This is to make sure that we always put a new value back.

struct VersionChangeCap
Fields
versioned_id: object::ID
old_version: u64

Constants

Failed to upgrade the inner object due to invalid capability or new version.

const EInvalidUpgrade: u64 = 0;

Function create

Create a new Versioned object that contains a initial value of type T with an initial version.

public fun create<T: store>(init_version: u64, init_value: T, ctx: &mut tx_context::TxContext): versioned::Versioned
Implementation
public fun create<T: store>(init_version: u64, init_value: T, ctx: &mut TxContext): Versioned {
    let mut self = Versioned {
        id: object::new(ctx),
        version: init_version,
    };
    dynamic_field::add(&mut self.id, init_version, init_value);
    self
}

Function version

Get the current version of the inner type.

public fun version(self: &versioned::Versioned): u64
Implementation
public fun version(self: &Versioned): u64 {
    self.version
}

Function load_value

Load the inner value based on the current version. Caller specifies an expected type T. If the type mismatch, the load will fail.

public fun load_value<T: store>(self: &versioned::Versioned): &T
Implementation
public fun load_value<T: store>(self: &Versioned): &T {
    dynamic_field::borrow(&self.id, self.version)
}

Function load_value_mut

Similar to load_value, but return a mutable reference.

public fun load_value_mut<T: store>(self: &mut versioned::Versioned): &mut T
Implementation
public fun load_value_mut<T: store>(self: &mut Versioned): &mut T {
    dynamic_field::borrow_mut(&mut self.id, self.version)
}

Function remove_value_for_upgrade

Take the inner object out for upgrade. To ensure we always upgrade properly, a capability object is returned and must be used when we upgrade.

public fun remove_value_for_upgrade<T: store>(self: &mut versioned::Versioned): (T, versioned::VersionChangeCap)
Implementation
public fun remove_value_for_upgrade<T: store>(self: &mut Versioned): (T, VersionChangeCap) {
    (
        dynamic_field::remove(&mut self.id, self.version),
        VersionChangeCap {
            versioned_id: object::id(self),
            old_version: self.version,
        },
    )
}

Function upgrade

Upgrade the inner object with a new version and new value. Must use the capability returned by calling remove_value_for_upgrade.

public fun upgrade<T: store>(self: &mut versioned::Versioned, new_version: u64, new_value: T, cap: versioned::VersionChangeCap)
Implementation
public fun upgrade<T: store>(
    self: &mut Versioned,
    new_version: u64,
    new_value: T,
    cap: VersionChangeCap,
) {
    let VersionChangeCap { versioned_id, old_version } = cap;
    assert!(versioned_id == object::id(self), EInvalidUpgrade);
    assert!(old_version < new_version, EInvalidUpgrade);
    dynamic_field::add(&mut self.id, new_version, new_value);
    self.version = new_version;
}

Function destroy

Destroy this Versioned container, and return the inner object.

public fun destroy<T: store>(self: versioned::Versioned): T
Implementation
public fun destroy<T: store>(self: Versioned): T {
    let Versioned { mut id, version } = self;
    let ret = dynamic_field::remove(&mut id, version);
    id.delete();
    ret
}