@mysten/sui v2.0 and a new dApp Kit are here! Check out the migration guide
Mysten Labs SDKs

Derived Objects

Compute derived object IDs from parent objects

Derived objects enable deterministic IDs for objects, enabling offline derivation of object IDs. Click here to read more.

To derive an object ID, you can import deriveObjectID function exposed from utils.

import { deriveObjectID } from '@mysten/sui/utils';

To derive any object, you need to have its parent's ID (the object from which it was derived), and the key used to generate it.

It is recommended to verify the on-chain derived_object::derive_address match your off-chain calculation (at least once when implementing offline calculations), especially for critical cases like transferring assets.

Deriving using primitive keys

To derive the IDs using primitive types, you can use the built-in types like this, assuming you have a parent object with ID 0xc0ffee.

// Example 1: On-chain derivation for `0xc0ffee + vector<u8>([0,1,2])
deriveObjectID('0xc0ffee', 'vector<u8>', bcs.vector(bcs.u8()).serialize([0, 1, 2]).toBytes());

// Example 2: On-chain derivation for `0xc0ffee + address('0x111')`
deriveObjectID('0xc0ffee', 'address', bcs.Address.serialize('0x111').toBytes());

// Example 3: On-chain derivation for `0xc0ffee + non-ascii string ("foo")`
deriveObjectID('0xc0ffee', '0x1::string::String', bcs.String.serialize('foo').toBytes());

Deriving using custom types

To derive IDs using your custom objects, you can use BCS & the known type IDs.

Assuming a custom struct on-chain (for the key) being:

public struct DemoStruct has copy, store, drop { value: u64 }

you can derive it by doing:

// Assuming we wanted to derive for key `DemoStruct { value: 1 }`.
const bcsType = bcs.struct('DemoStruct', {
	value: bcs.u64(),
});

const key = bcsType.serialize({ value: 1 }).toBytes();

// Derive the object ID for the key `DemoStruct { value: 1 }`.
deriveObjectID('0xc0ffee', `0xc0ffee::demo::DemoStruct`, key);

Deriving with fieldless structs

In Move, structs with no user-defined fields automatically get a dummy_field: bool injected by the compiler. This means their BCS encoding is not empty — it's a single 0x00 byte (the encoding of false). You can treat this as a constant.

A real-world example is 0x2::coin_registry::CurrencyKey<T>, which is a fieldless generic struct used to derive Currency objects from the coin registry (0xc) based on their type:

// On-chain: `public struct CurrencyKey<phantom T>() has copy, store, drop`
// The compiler injects `dummy_field: bool`, always `false`.
const key = new Uint8Array([0]);

const coinType = '0x2::sui::SUI';
const currencyId = deriveObjectID('0xc', `0x2::coin_registry::CurrencyKey<${coinType}>`, key);

This applies to any struct with no fields — phantom type parameters don't add any BCS-encoded data, so the key is always new Uint8Array([0]).

On this page