Shared Objects
A shared object is a public, mutable object that any address on the network can reference in a transaction. Multiple users and smart contracts can interact with the same shared object concurrently. Shared objects go through consensus ordering, which adds latency compared to address-owned objects but enables concurrent multi-party access.
Create shared objects
Use the sui::transfer::share_object function to create a shared object, making it publicly accessible on the network. Extended functionality and accessibility of shared objects requires additional effort by securing access, if needed.
Shared objects require the key ability.
public struct Donut has key { id: UID }
fun init(ctx: &mut TxContext) {
transfer::transfer(ShopOwnerCap {
id: object::new(ctx)
}, ctx.sender());
transfer::share_object(DonutShop {
id: object::new(ctx),
price: 1000,
balance: balance::zero()
})
When to use shared objects
Shared objects are ideal for things like marketplaces, games, or other scenarios where global state needs to be accessed or modified by multiple parties.
Interact with shared objects
The following example creates a shop to sell digital donuts. Everyone needs access to the shop to purchase donuts from it, so the example creates the shop as a shared object using sui::transfer::share_object.
module examples::donuts;
use sui::sui::SUI;
use sui::coin::{Self, Coin};
use sui::balance::{Self, Balance};
/// For when Coin balance is too low.
const ENotEnough: u64 = 0;
/// Capability that grants an owner the right to collect profits.
public struct ShopOwnerCap has key { id: UID }
/// A purchasable Donut. For simplicity's sake we ignore implementation.
public struct Donut has key { id: UID }
/// A shared object. `key` ability is required.
public struct DonutShop has key {
id: UID,
price: u64,
balance: Balance<SUI>
}
/// Init function is often ideal place for initializing
/// a shared object as it is called only once.
fun init(ctx: &mut TxContext) {
transfer::transfer(ShopOwnerCap {
id: object::new(ctx)
}, ctx.sender());
// Share the object to make it accessible to everyone!
transfer::share_object(DonutShop {
id: object::new(ctx),
price: 1000,
balance: balance::zero()
})
}
/// Entry function available to everyone who owns a Coin.
public fun buy_donut(
shop: &mut DonutShop, payment: &mut Coin<SUI>, ctx: &mut TxContext
) {
assert!(coin::value(payment) >= shop.price, ENotEnough);
// Take amount = `shop.price` from Coin<SUI>
let paid = payment.balance_mut.split(shop.price);
// Put the coin to the Shop's balance
shop.balance.join(paid);
transfer::transfer(Donut {
id: object::new(ctx)
}, ctx.sender())
}
/// Consume donut and get nothing...
public fun eat_donut(d: Donut) {
let Donut { id } = d;
id.delete();
}
/// Take coin from `DonutShop` and transfer it to tx sender.
/// Requires authorization with `ShopOwnerCap`.
public fun collect_profits(
_: &ShopOwnerCap, shop: &mut DonutShop, ctx: &mut TxContext
) {
let amount = shop.balance.value();
let profits = shop.balance.split(amount).into_coin(ctx);
transfer::public_transfer(profits, ctx.sender())
}
Security considerations
Anyone can submit a transaction that references a shared object. The Sui runtime does not enforce access control on shared objects. Your Move code must verify authorization for every privileged operation. Learn more in (Security Best Practices).
Performance considerations
Transactions that touch shared objects take longer to finalize than address-owned-object transactions because they require consensus.
When many transactions write to the same shared object in the same checkpoint, they are sequenced rather than executed in parallel. This reduces throughput for that object. If your use case requires high write throughput, consider splitting state across multiple shared objects (for example, sharding an order book by price range) or using address-owned objects where possible.
Query shared objects offchain
You can read shared objects offchain using the same methods as any other Sui object. Use the object's ID to query its current state:
- GraphQL RPC: Use the
objectquery with the shared object's ID. See the GraphQL RPC reference for the full schema. - gRPC: Use the
GetObjectmethod. See the gRPC reference for details. - Event subscriptions: Subscribe to events emitted by the shared object to react to state changes in real time.
When to use address-owned objects instead
Prefer address-owned objects over shared objects when:
- Only one user needs to write to the object at a time.
- You need the lowest possible transaction latency.
- You want to avoid consensus overhead and potential contention.
Shared objects are the right choice when multiple parties must read and write the same mutable state. For read-only data that never changes, use immutable objects instead.