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]).