Skip to main content

Implementing the ID Pointer Pattern

In this tutorial, we will demonstrate how to implement the ID Pointer pattern in the Sui ecosystem. The ID Pointer pattern separates an object's main data…

Share
Last updated on August 20, 2026

In this tutorial, we will demonstrate how to implement the ID Pointer pattern in the Sui ecosystem. The ID Pointer pattern separates an object's main data and its accessors/capabilities by linking the latter to the original. This pattern can be used in various applications, such as issuing transferable capabilities for shared objects, splitting dynamic data and static data, and avoiding unnecessary type linking in generic applications.

For this example, we will implement a simple Lock and Key mechanics on Sui, where a Lock<T> is a shared object that can contain any object, and a Key is an owned object required to access the contents of the lock.

Follow these steps to implement the ID Pointer pattern:

  1. Create the lock_and_key module: Define a new module examples::lock_and_key that will implement the ID Pointer pattern.
rust
module examples::lock_and_key {
    use sui::object::{Self, ID, UID};
    use sui::transfer;
    use sui::tx_context::{Self, TxContext};
    use std::option::{Self, Option};
}
  1. Define constants and errors: Define error codes for the lock being empty, the key not matching the lock, and the lock being full.
rust
const ELockIsEmpty: u64 = 0;
const EKeyMismatch: u64 = 1;
const ELockIsFull: u64 = 2;
  1. Define the Lock and Key structs: Create a Lock struct that stores any content inside it and a Key struct that is linked to its Lock using an ID field.
rust
struct Lock<T: store + key> has key {
    id: UID,
    locked: Option<T>
}
 
struct Key<phantom T: store + key> has key, store {
    id: UID,
    for: ID,
}
  1. Create the key_for function: Define a public function key_for that takes a reference to a Key<T> and returns the ID of the associated Lock.
rust
public fun key_for<T: store + key>(key: &Key<T>): ID {
    key.for
}
  1. Create the create function: Define an entry function create that takes an object of type T and a mutable transaction context as arguments. It creates a new Lock with the given object and a Key linked to the lock, then transfers the key to the transaction sender.
rust
public entry fun create<T: store + key>(obj: T, ctx: &mut TxContext) {
    let id = object::new(ctx);
    let for = object::uid_to_inner(&id);
 
    transfer::share_object(Lock<T> {
        id,
        locked: option::some(obj),
    });
 
    transfer::transfer(Key<T> {
        for,
        id: object::new(ctx)
    }, tx_context::sender(ctx));
}
  1. Create the lock and unlock functions: Define entry functions lock and unlock that take an object of type T, a mutable reference to a Lock<T>, and a reference to a Key<T> as arguments. The lock function locks the object inside the shared lock if it's empty and the key matches the lock. The unlock function unlocks the lock and returns its contents if the key matches the lock and it's not empty.
rust
public entry fun lock<T: store + key>(
    obj: T,
    lock: &mut Lock<T>,
    key: &Key<T>,
) {
    assert

One API key for 30+ chains

bex router is a single multi-chain gateway — one key, one bill, metered in compute units instead of an account per network.

Explore bex router