Sui.

Post

Share your knowledge.

shamueely.
Jul 30, 2025
Expert Q&A

Design Patterns and Concurrency Management for Shared Objects in Sui Move

I’m trying to wrap my head around the right architectural pattern when working with shared objects in Sui Move, especially when multiple users need to interact with them concurrently (e.g., in a game arena, DAO vote tracker, or lending vault).

I’ve read that shared objects can introduce synchronization issues, but I’m still unclear on:

  • When exactly should I use a shared object vs. owned?
  • How does Sui handle concurrent transaction attempts on the same shared object — do I need to manually queue/lock things?
  • What’s the best practice for designing modules that support high concurrency but avoid frequent ConflictTransaction errors?
  • Should I be doing anything special (like splitting mutable fields into sub-objects) to reduce contention?
  • Sui
  • Architecture
  • NFT Ecosystem
  • Move
2
13
Share
Comments
.

Answers

13
theking.
Jul 30 2025, 10:21

When you're building a dApp on Sui where multiple users need to interact with the same state, like in a game arena, lending protocol, or DAO, you should use shared objects instead of owned ones. Shared objects are publicly accessible and mutable, meaning anyone can call entry functions on them as long as they’re in the global object graph. In contrast, owned objects are private to a user and are ideal for personal assets like wallets or inventory. So, you should use a shared object when state updates must be visible and usable by many users at once.

Sui handles concurrent transactions on shared objects through a mechanism called causal order validation, which requires you to explicitly provide the latest known version of a shared object. If two users try to modify the same shared object at the same time with outdated versions, one will succeed while the others fail with a ConflictTransaction error. Sui doesn’t queue or lock shared objects natively, so you need to design your Move modules to minimize overlapping writes. One of the best ways to reduce conflict is to design for parallelism. For instance, instead of storing all mutable state in the root shared object, you can split high-contention fields into separate child objects. Each of those children can then be written independently, reducing the likelihood of transaction conflicts.

A great practice is to keep the main shared object as a directory and move granular, frequently updated state—like player scores, votes, or loan balances—into dynamic fields or smaller sub-objects. This way, users update only the specific part they need, and not the whole shared object. It’s also smart to separate read-heavy and write-heavy components. Moreover, you should build clear and deterministic transaction flows that always use the freshest version of shared objects and retry logic for clients to handle failed transactions.

You can learn more about shared object design and conflict mitigation in the Sui documentation here: https://docs.sui.io/concepts/objects#shared-objects.

If you're managing something like a vote tracker, here's a simplified transaction block example to reduce conflict:

const tx = new TransactionBlock();
tx.moveCall({
  target: `${packageId}::vote_tracker::cast_vote`,
  arguments: [
    tx.sharedObject(voteTrackerId),         // shared object reference
    tx.pure(voterId),                       // owned object or public key
    tx.pure(proposalId),                    // pure data
    tx.pure(voteChoice)
  ]
});
1
Best Answer
Comments
.
Benjamin XDV.
Jul 31 2025, 09:52

In Sui Move, shared objects (marked with key + store) are essential for multi-user interaction but require careful concurrency design. Use them when state must be globally mutable (e.g., games, DAOs), while owned/immutable objects suit single-user scenarios. Sui serializes transactions per shared object automatically, but frequent writes may trigger ConflictTransaction errors—mitigate this by splitting hot fields into separate child objects (e.g., one per user) or batching updates. For high throughput, adopt event-sourcing patterns: store actions as immutable events and aggregate state periodically. Always minimize mutable shared fields and prefer atomic swaps via transfer::share_object over direct mutations.

8
Comments
.
Paul.
Paul4300
Jul 31 2025, 05:46

In Sui Move, handling shared objects and concurrency effectively requires careful design to avoid race conditions and high contention. Here's a breakdown of the best practices and architectural patterns:

1. When to Use Shared vs. Owned Objects:

  • Shared Objects: Use when multiple users need to interact with the same object concurrently (e.g., shared game state, DAO vote tracker).
  • Owned Objects: Use when the object is specific to a single user or account, avoiding shared state and reducing concurrency issues.

2. Concurrent Transaction Handling in Sui:

  • Sui handles concurrency automatically by using Transaction Locks to prevent simultaneous modifications of the same object.
  • You do not need to manually queue or lock shared objects, as Sui enforces a lock at the object level. However, you might get ConflictTransaction errors if multiple transactions conflict on the same object.

3. Best Practices for High Concurrency:

  • Minimize Contention: Split mutable fields into separate sub-objects to allow parallel updates to different parts of the object.
  • Avoid Hotspots: Distribute frequently modified state across multiple objects (e.g., instead of having a single shared ledger, split it into multiple buckets or accounts).
  • Versioning: Use versioned objects or create new objects to track updates instead of mutating the same object repeatedly.

4. Managing Contention:

  • Consider splitting mutable fields into smaller objects to reduce contention. This allows different parts of the object to be updated concurrently, reducing the chance of conflicts.
  • Use atomic transactions to bundle related actions into one, ensuring consistency across related objects.

Example:

If you have a vote tracker for a DAO:

  • Instead of storing all votes in a single shared object, split them into multiple objects (e.g., one per proposal), allowing parallel updates for each proposal while reducing contention.

Conclusion:

For high concurrency, minimize contention by distributing state, using sub-objects, and leveraging Sui’s automatic transaction handling. Ensure you understand your access patterns and structure the objects to minimize conflict points.

7
Comments
.
Ashford.
Jul 31 2025, 07:37

Understanding Design Patterns and Concurrency Management for Shared Objects in Sui Move

When dealing with shared objects in Sui Move, managing concurrency becomes critical, as multiple users may need to interact with the same object simultaneously. This is especially true for use cases like game arenas, DAO vote trackers, and lending vaults, where high concurrency is expected. The Sui Network provides some features to help manage concurrency, but it still requires careful design to avoid common pitfalls, like ConflictTransaction errors.

1. When to Use Shared Objects vs. Owned Objects

  • Owned Objects:

    • An object is owned if it’s managed by a single address, which can transfer or modify it.
    • Use owned objects when you expect a single entity to control and interact with an object. For example, if you have a wallet with coins, each wallet has ownership over its own coins.
  • Shared Objects:

    • Shared objects can be accessed and modified by multiple addresses simultaneously. Sui allows you to define objects as shared by making them mutable and providing access to multiple parties.

    • Use shared objects for scenarios where multiple users or addresses need to interact with the same object, such as:

      • DAO vote trackers: Multiple users may vote on the same object.
      • Lending vaults: Multiple users can lend and borrow from the same vault.
      • Game arenas: Multiple users interact with the game state at the same time.

Key Consideration: Shared objects should be used carefully, as they can introduce contention — situations where multiple transactions are trying to access and modify the same object at once.

2. Concurrency and Transaction Attempts on Shared Objects

Sui ensures that concurrent transactions on the same shared object are handled correctly by enforcing transactional isolation. If multiple transactions attempt to mutate the same shared object at the same time, one of them will fail due to contention.

When a transaction attempts to modify a shared object that’s already being modified by another transaction, Sui will return a ConflictTransaction error. However, Sui handles most of the concurrency management internally, and you do not need to manually queue or lock objects. Instead, you need to design your contract to minimize contention and avoid situations where two transactions frequently conflict.

3. Best Practices for High Concurrency and Reducing ConflictTransaction Errors

To design modules that can handle high concurrency and avoid frequent ConflictTransaction errors, follow these best practices:

a. Minimize Contention Points:

  • Limit mutable access: Keep the number of mutable fields in shared objects as small as possible. Each mutable field introduces a potential point of contention.
  • Use owned objects for independent data: Instead of making everything shared, store independent data in owned objects. For instance, if you have user-specific data (like balance), store it in an owned object rather than a shared one.

b. Use the "State Partitioning" Pattern:

  • Split your shared object into multiple sub-objects to reduce contention. For example, instead of having one shared object that tracks the entire game state, you could split it into several sub-objects:

    • One for each player's state.
    • One for the game settings.
    • One for leaderboard data.
  • This way, transactions related to different parts of the system can occur concurrently without blocking each other.

c. Batch Operations:

  • When working with shared objects, batch your updates to reduce the number of transactions that need to interact with the object. This can help avoid frequent conflicts.
  • Use collections like vectors or sets to manage multiple updates in a single transaction.

d. Use Atomic Operations:

  • Whenever possible, design your modules to use atomic operations where possible. For example, instead of having separate transactions for checking a condition and then updating a field, bundle the checks and updates into a single atomic operation.

e. Retry Logic:

  • While Sui handles retries internally, you can implement retry logic at the application level to handle occasional contention errors. For instance, if a ConflictTransaction error occurs, you can simply retry the transaction or queue it for later execution.

4. Reducing Contention by Splitting Mutable Fields into Sub-Objects

This is a highly recommended practice. When dealing with shared objects, the more fields you have in a shared object that are mutable, the higher the chance that two transactions will try to mutate the same field at the same time.

  • Example 1: If you have a shared object representing a lending vault, split it into sub-objects:

    • One for deposits: Track individual deposits separately.
    • One for loans: Track individual loans separately.
    • One for vault state: Track general state info (e.g., total value, interest rate).

This way, a transaction that modifies one sub-object (e.g., the loan sub-object) does not conflict with a transaction that modifies another (e.g., the deposit sub-object).

  • Example 2: In a game arena:

    • Split the game state into sub-objects for each player, game state, and battle statistics. This reduces the risk of conflicts when different players interact with their individual game states.

By splitting mutable fields into sub-objects, you reduce the likelihood that multiple users will try to modify the same object simultaneously.

5. Other Techniques to Manage Concurrency in Sui

  • Locking (Explicit Locks): While Sui doesn't require you to manually handle locks, in some cases, you may want to enforce explicit locks using locking objects (separate objects that ensure only one transaction can access a particular resource at a time).

  • Temporal Isolation: If your objects only change occasionally, consider introducing time-based isolation. For example, if you’re managing a vault or a voting system, set voting periods or transaction windows to limit interactions during certain times.

6. Example Code for Managing Concurrency with Sub-Objects

Here’s an example illustrating how to reduce contention by splitting a lending vault into smaller sub-objects:

module LendingVault {

    struct Deposit has store {
        amount: u64,
        depositor: address,
    }

    struct Loan has store {
        amount: u64,
        borrower: address,
    }

    struct VaultState has store {
        total_value: u64,
        interest_rate: u64,
    }

    struct LendingVault has store {
        deposits: vector<Deposit>,
        loans: vector<Loan>,
        vault_state: VaultState,
    }

    // Function to add a deposit
    public fun add_deposit(vault: &mut LendingVault, depositor: address, amount: u64) {
        let deposit = Deposit { amount, depositor };
        Vector::push_back(&mut vault.deposits, deposit);
        vault.vault_state.total_value = vault.vault_state.total_value + amount;
    }

    // Function to add a loan
    public fun add_loan(vault: &mut LendingVault, borrower: address, amount: u64) {
        let loan = Loan { amount, borrower };
        Vector::push_back(&mut vault.loans, loan);
        vault.vault_state.total_value = vault.vault_state.total_value - amount;
    }
}

Summary

  • Use shared objects when multiple users need to interact with the same object concurrently (e.g., DAO votes, game state, lending vaults).
  • Minimize contention by splitting large shared objects into smaller sub-objects and reducing the number of mutable fields.
  • Avoid frequent ConflictTransaction errors by following best practices such as state partitioning, batching operations, and using atomic operations.
  • Retry logic and optimistic concurrency control can be used to handle conflicts gracefully.
  • Sui's transaction model and conflict resolution are robust, but the design of your shared objects is key to ensuring efficient concurrent access.

By following these design principles, you'll be able to manage high concurrency in your Move-based modules while avoiding the typical pitfalls like conflicts and transaction errors.

7
Comments
.
Evgeniy CRYPTOCOIN.
Jul 31 2025, 09:29

Use shared objects for multi-writer access (e.g., games, DAOs). Key patterns:

  1. Owned vs Shared

    • Owned: Single-writer (faster).
    • Shared: Concurrent writes (higher gas).
  2. Concurrency Handling

    • Sui sequences shared object TXs automatically (no manual locks).
    • Split hot data into sub-objects (e.g., 1 per player) to reduce conflicts.
  3. Conflict Mitigation

    • Isolate high-traffic fields (e.g., Table<ID, PlayerData>).
    • Use PTBs for atomic multi-object ops.

Watch For:

  • ConflictTransaction: Split data or batch updates.
  • Gas spikes on shared objects.

(Sui’s runtime handles conflicts—optimize via data sharding.)

6
Comments
.
Alya.
Alya-14
Jul 30 2025, 17:38

Use shared objects (transfer::share_object) when multiple users must mutate the same object (e.g., vault, DAO, game state). Use owned otherwise.

Sui handles concurrency via Narwhal & Tusk consensus: conflicting transactions on a shared object are serialized—only one succeeds per round; others fail with TransactionLockConflict. No manual locking needed, but you must design for retries.

Best practices:

  • Minimize mutable state on shared objects; split hotspots into separate sub-objects (e.g., per-user vault shares).
  • Use VecMap or Table for scalable mappings.
  • Avoid long-running computations in shared functions.
  • Emit events instead of storing ephemeral state.
  • For high-contention systems, use batching or commit-reveal schemes to reduce conflicts.

Shared objects are globally accessible but incur higher gas and contention—optimize for idempotency and retry logic in client code.

5
Comments
.
Arnold.
Arnold3036
Jul 31 2025, 08:34

For shared objects in Sui Move, use these patterns to manage concurrency:

1. Shared vs. Owned?

  • Shared Object (key + store):

    • Use for global state (e.g., games, DAOs).
    • Allows parallel writes (unlike EVM’s serialized TXs).
    struct GameArena has key, store { id: UID, players: vector<address> }
    
  • Owned Object:

    • Use for user-specific data (e.g., NFTs, wallets).
    struct PlayerInventory has key { id: UID, items: vector<Item> }
    

2. Concurrency Handling

  • No Manual Locks: Sui resolves conflicts automatically (unlike EVM).
  • Split Contention: Break shared objects into smaller sub-objects (e.g., per-player or per-shard).
    struct PlayerScore has key, store { id: UID, points: u64 } // Less contention
    

3. Reduce ConflictTransaction Errors

  • Isolate Writes: Update different fields in separate TXs.
  • Use &mut Wisely: Avoid broad mutations.
    public entry fun update_score(
        arena: &mut GameArena,
        player: address,
        points: u64
    ) { /* ... */ } // Only touches 1 player’s data
    

4. Best Practices

  • Batch Updates: Group non-conflicting ops (e.g., votes in a DAO).
  • Event-Driven: Emit events instead of mutating state where possible.
    struct VoteEvent has copy, drop { voter: address, choice: u8 }
    
5
Comments
.
JK spike.
Aug 15 2025, 12:39

Use shared objects for multi-writer access (e.g., games, DAOs). Key patterns:

Owned vs Shared

Owned: Single-writer (faster). Shared: Concurrent writes (higher gas). Concurrency Handling

Sui sequences shared object TXs automatically (no manual locks). Split hot data into sub-objects (e.g., 1 per player) to reduce conflicts. Conflict Mitigation

Isolate high-traffic fields (e.g., Table<ID, PlayerData>). Use PTBs for atomic multi-object ops. Watch For:

ConflictTransaction: Split data or batch updates. Gas spikes on shared objects. (Sui’s runtime handles conflicts—optimize via data sharding.)

3
Comments
.
Jeff.
Jeff1911
Aug 23 2025, 06:03

In Sui Move, shared objects (marked with key + store) are essential for multi-user interaction but require careful concurrency design. Use them when state must be globally mutable (e.g., games, DAOs), while owned/immutable objects suit single-user scenarios. Sui serializes transactions per shared object automatically, but frequent writes may trigger ConflictTransaction errors—mitigate this by splitting hot fields into separate child objects (e.g., one per user) or batching updates. For high throughput, adopt event-sourcing patterns: store actions as immutable events and aggregate state periodically. Always minimize mutable shared fields and prefer atomic swaps via transfer::share_object over direct mutations

3
Comments
.
Bekky.
Bekky1762
Jul 31 2025, 12:26

Shared vs. Owned Objects: When to Use Each

Use shared objects when:

  • Multiple users need to mutate the same state
  • The object represents a public resource (like a game arena or DAO)
  • You need permissionless access patterns

Use owned objects when:

  • Only one address should control the object
  • You want to leverage Sui's parallel execution fully
  • Objects represent user-specific assets

Concurrency Management Strategies

1. Sub-object Pattern (Reduce Contention)

Split frequently-updated fields into separate objects:

struct GameArena has key {
    id: UID,
    // Immutable or rarely changed fields
    config: ArenaConfig,
    // Split mutable state
    player_state: Table<address, PlayerState>,
    leaderboard: Leaderboard
}

struct PlayerState has key, store {
    id: UID,
    health: u8,
    score: u64
}

2. Batch Updates with Vectors/Tables

struct DAOVault has key {
    id: UID,
    // Store multiple pending actions
    pending_votes: Table<ID, Vote>,
    pending_deposits: Table<address, Deposit>
}

public entry fun batch_process(
    vault: &mut DAOVault,
    ctx: &mut TxContext
) {
    // Process all pending items at once
    let votes = table::remove_all(&mut vault.pending_votes);
    process_votes(votes);
}

3. Epoch-based Processing

struct LendingPool has key {
    id: UID,
    current_epoch: u64,
    next_epoch_data: EpochData,
    current_epoch_data: EpochData
}

public entry fun advance_epoch(pool: &mut LendingPool) {
    let finished = pool.current_epoch_data;
    pool.current_epoch_data = pool.next_epoch_data;
    pool.next_epoch_data = new_epoch_data();
    process_finished_epoch(finished);
}

Conflict Avoidance Techniques

  1. Field-level Partitioning:

    struct HighTrafficObject has key {
        id: UID,
        // Split into separate objects per "hot" field
        stats_by_category: Table<u8, CategoryStats>
    }
    
  2. Delayed Execution Queue:

    struct ActionQueue has key {
        id: UID,
        pending: VecMap<u64, Action>
    }
    
    public entry fun execute_ready_actions(
        queue: &mut ActionQueue,
        ready_up_to: u64
    ) {
        // Processes actions in batches
    }
    
  3. Optimistic Concurrency with Versioning:

    struct Versioned has key {
        id: UID,
        version: u64,
        data: vector<u8>
    }
    
    public entry fun update(
        obj: &mut Versioned,
        new_data: vector<u8>,
        expected_version: u64
    ) {
        assert!(obj.version == expected_version, EBAD_VERSION);
        obj.data = new_data;
        obj.version = expected_version + 1;
    }
    

Practical CLI Examples

Checking object dependencies before submission:

sui client inspect-transaction --serialize-unsigned <TX_BYTES>

Simulating contention scenarios:

sui move test --path <YOUR_PACKAGE> --gas-budget 10000

Best Practices Summary

  1. Minimize Shared Object Mutations: Design for infrequent writes to shared objects
  2. Use Child Objects: Decompose state to reduce contention points
  3. Batch Operations: Group related changes into single transactions
  4. Consider Hybrid Models: Combine owned objects with occasional shared coordination
  5. Monitor Hotspots: Use sui-tool replay to analyze contention

Remember that Sui's runtime automatically handles some concurrency through its object model, but you still need to architect your data structures to minimize logical conflicts.

2
Comments
.
24p30p.
24p30p2038
Jul 31 2025, 05:25

You should use a shared object in Sui Move when multiple users need to interact with the same piece of state, and those users aren't all under the same owner. Examples include a game arena open to all players, a DAO vote tracker that collects votes from different addresses, or a lending pool that anyone can deposit into. Shared objects are globally accessible and updatable through consensus, unlike owned objects, which are tied to a single address and modified without full consensus.

Sui handles concurrency on shared objects by requiring full consensus for any transaction that mutates them. When two users try to mutate the same shared object at nearly the same time, only one transaction succeeds—others get rejected with a ConflictTransaction error. This makes shared objects powerful but also introduces contention issues when not designed carefully.

To support high-concurrency without running into frequent ConflictTransaction errors, you should use design patterns that isolate mutable state. Instead of storing all mutable data in a single shared object, split it into multiple child objects—each with its own ownership or shared status. For example, in a game arena:

struct Arena has key, store {
    game_id: u64,
    players: vector<address>,
}

struct PlayerState has key, store {
    hp: u64,
    energy: u64,
}

Make Arena shared, but give each PlayerState its own shared object. That way, different players can update their state independently without conflicting.

Another pattern is the “index + bucket” strategy: keep a shared index object that points to individual buckets or entries. Only access the index to fetch the right entry, then operate on the entry itself. This reduces the amount of overlap between user transactions.

If you need strict ordering (e.g., for auctions), implement manual queuing using vectors or maps inside the shared object—but know that each mutation still needs consensus and could fail under load. In those cases, consider offloading ordering to the frontend or batching writes.

In summary:

  • Use shared objects when state must be accessed by many independent users.
  • Avoid putting all mutable fields into a single shared object—split them into sub-objects.
  • Use shared + dynamic child objects to lower conflict rates.
  • Expect ConflictTransaction errors under high contention and retry on the frontend.
  • Prefer append-only updates where possible, rather than in-place mutation.
0
Comments
.
yungrazac.
Aug 22 2025, 10:03

When you design with shared objects in Sui Move, you’re essentially choosing between convenience for multi-user access and the risk of contention. Since Sui uses an object-centric and resource-oriented model, the right pattern depends on whether your use case demands collaborative access or isolated control. Here’s a structured way to think about it:


When to Use Shared vs. Owned Objects

  • Owned objects are best when a single user needs full control over an asset (e.g., a wallet’s coins, a player’s inventory, or a unique NFT). Transactions involving them won’t conflict unless that same object is reused.
  • Shared objects make sense when multiple users need to read or write to the same state (e.g., a DAO governance record, a game arena leaderboard, a lending pool vault). They allow global accessibility without needing permissioned transfers, but they come with concurrency challenges.

Concurrency Management in Sui

Sui’s runtime automatically serializes transactions that touch the same mutable shared object, meaning if two users try to update the same shared object, one will succeed and the other will get a ConflictTransaction error. You don’t need to manually code locks or queues—conflict resolution is handled at the execution layer. However, frequent conflicts can degrade throughput, so your job is to design state layouts that minimize overlaps.


Best Practices to Avoid Frequent Conflicts

  • Split mutable state: Instead of a single “mega-object,” break it down into smaller child objects. For instance, in a lending vault, separate each user’s position as its own owned or partially shared object, leaving the vault itself mostly static. This reduces write contention.
  • Use immutable fields where possible: Store metadata or reference data in immutable sub-objects so transactions that only read don’t collide with those that write.
  • Batch operations carefully: If your contract design requires multiple writes to the same shared object, consider batching them into a single transaction block to avoid multiple conflicts.
  • Sharding patterns: For things like leaderboards or DAOs, shard state by group, round, or identifier so that not all transactions hit the exact same object path.

Pitfalls to Watch Out For

  • Monolithic shared objects: Putting everything in one shared object causes high contention and constant conflicts under load.
  • Overusing shared objects: If the data can be modeled as owned, prefer that route—it’s cheaper and less likely to bottleneck.
  • Ignoring access patterns: If most interactions are reads with few writes, design around immutable snapshots rather than one mutable shared state.

Practical Example

Imagine building a DAO vote tracker:

  • Bad pattern: A single shared object storing every vote, updated by all participants.
  • Better pattern: Each voter creates an owned Vote object referencing the proposal, while the shared DAO object only aggregates results periodically. This way, parallel voting doesn’t collide.

Transaction Block

Inputs → Users submit transactions against shared or owned objects. Process → Sui’s runtime serializes transactions on the same mutable shared object. Result → Successful writes are committed, others get conflict errors; splitting objects lowers the chance of conflicts.


0
Comments
.
Sato$hii.
Aug 23 2025, 00:02

Great questions—this is exactly the crux of building “high-fanout” apps on Sui. Here’s a practical playbook you can follow.

🧭 When to use a shared object (vs owned)

Use a shared object only when multiple independent parties must mutate the same piece of state and you need one canonical result (e.g., AMM pool reserves, an arena’s match registry, a DAO proposal’s tally). Prefer owned objects when the state is per-user, per-position, or per-item and can evolve independently (inventories, user balances, open positions).

Rule of thumb: put the coordination surface in a shared object, and everything else in owned objects that can run in parallel.


⚙️ What Sui does with concurrent access

  • Any tx that mutates a shared object goes through consensus and is totally ordered. Conflicting writes to the same fields are serialized; others proceed in parallel.
  • You don’t manually lock; you design to avoid hotspots. Conflicts show up as ConflictTransaction when many txs try to bump the same version.

🏗️ High-concurrency design patterns (with Move tips)

1) Root-manager + owned positions

Keep a minimal shared root; put user/position state in owned objects so deposits/claims mostly touch owned data.

module example::vault {
    use sui::tx_context::TxContext;

    struct Vault has key { /* config only; no growing vectors */ }
    struct Position has key { owner: address, /* state */ }

    /// Shared root only needed to mint/close positions or checkpoint.
    public entry fun open_position(v: &mut Vault, ctx: &mut TxContext): Position { /* ... */ }

    /// Hot path touches owned Position only → parallel.
    public entry fun deposit(pos: &mut Position, amount: u64) { /* ... */ }
}

Why it scales: most traffic avoids the shared root altogether.


2) Shard by key (dynamic fields / tables)

Instead of one big shared map, create N shard objects (shared) under a registry and route by hash(key) % N. Each shard is a separate contention domain.

/// registry is shared; it stores IDs of shard objects
struct Registry has key { shards: vector<ID> }

/// choose shard once and keep it stable for that key
fun shard_for(reg: &Registry, key: &vector<u8>): ID { /* hash(key) % len */ }

Tip: don’t update the registry on every user op; mutate only the target shard.


3) Dynamic fields on a shared “anchor”, not growing vectors

Avoid vector.push on a shared object (resizes = conflicts). Use dynamic fields (key → value) so each entry mutates independently.

SharedAnchor
  └─ df[key=user] = tiny record (or pointer to owned object)

Move APIs: sui::dynamic_field::{add, borrow_mut, remove}.


4) Two-phase “ticket” pattern

Have the shared object mint a ticket/capability (owned) that authorizes a later operation done off the hot path.

  1. Light touch on shared root → mint Ticket.
  2. User completes heavy work by presenting the Ticket and mutating only owned/child objects.

Reduces churn on the shared root, and gives you idempotency (ticket can be one-time-use).


5) Append-only events, deferred aggregation

If you’re tempted to keep a total_* counter on the shared root (a hotspot), emit events on each action and aggregate off-chain or in a periodic checkpoint entry that rolls up per-shard totals.


6) Split mutable fields into sub-objects

If one field changes much more frequently (e.g., queue head), peel it into its own child object (owned or shared as needed). Then updates don’t bump the root’s version.


🧪 Avoiding ConflictTransaction in practice

  • Keep the cut set small: Each entry function should touch as few objects as possible.
  • No growing vectors on shared roots; use dynamic fields or sharded children.
  • Stable routing: Deterministic key→shard mapping; never rehash live keys.
  • Idempotency: Use tickets/nonces so retries don’t double-apply.
  • Batch carefully: If you must touch multiple entries atomically, use a PTB—but keep it within a single shard when possible.
  • Backoff & retry: On the client, exponential backoff for known conflict aborts.

🔍 Minimal conflict checklist

  • Shared root is configuration + pointers only.
  • Hot paths mutate owned positions or sharded children.
  • Per-user/per-item state is not inside the root.
  • No vector.push on shared; use dynamic fields.
  • Periodic checkpoint function consolidates, not every tx.

🛡️ Security & correctness notes

  • Gate privileged ops with capabilities (e.g., AdminCap, MintCap); store caps in owned objects, not globals.
  • Validate invariants in shared entry points (sum of shard totals, proposal status transitions, etc.).
  • Emit events for auditability; they are cheap and conflict-free.

Quick templates by use case

  • Game arena: Shared Arena (config/match index) + owned Player / Loadout. Match joins go to Shard[hash(match_id)].
  • DAO voting: Shared Proposal (immutable metadata) + dynamic fields per voter or owned Ballot tickets. Tally via periodic checkpoint.
  • Lending vault: Shared Registry + owned Positions; price/oracle reads are immutable; rebalances via sharded pools.

0
Comments
.

Do you know the answer?

Please log in and share it.

Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.

1166Posts3581Answers
Sui.X.Peera.

Earn Your Share of 1000 Sui

Gain Reputation Points & Get Rewards for Helping the Sui Community Grow.

Reward CampaignAugust