Sui.

Post

Share your knowledge.

Meaning.Sui.
Jul 27, 2025
Expert Q&A

fetch shared object versions in the Rust SDK

Building backends and PTBs almost always requires passing shared objects as arguments to move calls. In order to be used as a PTB input, we need to provide three pieces of information about the shared object:

Its ID Whether it should be passed as mutable A u64 representing the shared version Fetching the shared version u64 is not ideal from a developer experience perspective, it requires making an RPC request and handling all possible errors. This results in extra lines of code.

The TypeScript Sui SDK performs this automatically, while still allowing the developer to manually provide the shared version to avoid the extra request.

  • Sui
1
8
Share
Comments
.

Answers

8
harry phan.
Jul 27 2025, 12:02

Regarding BCS, there are multiple solutions for this

Solution 1: Modifying the ObjectArg enum, without its BCS impl We can add a new case to ObjectArg that we can call SharedObjectLatest (just an example name), without the field initial_shared_version. When preparing a Transaction struct from a ​TransactionData, we can retrieve the initial_shared_version and convert SharedObjectLatest to SharedObject

To make this secure, we would write a custom Serialize impl for ObjectArg such that we ensure SharedObjectLatest cannot be serialized, and other cases have the correct discriminant: 0 for ImmOrOwnedObject, 1 for SharedObject, and 2 for Receiving

Finally, methods such as Transaction::to_tx_bytes_and_signatures would return a ​Result, returning ​Err when bcs::to_bytes fails (currently there's an .unwrap() on it)

Solution 2: Adding a new function to ​ProgrammableTransactionBuilder We could add an async method ​shared_obj_latest on ​ProgrammableTransactionBuilder which would have the following signature:

pub async fn shared_obj_latest(&mut self, object_id: ObjectID, mutable: bool) -> anyhow::Result { ... } This function would fetch ​initial_shared_version and call ​self.obj(ObjectArg::SharedObject { ... })

Even better, to maximize developer experience, we can add ​initial_shared_version as an ​Option and rename the method to simply shared_obj:

pub async fn shared_obj( &mut self, object_id: ObjectID, mutable: bool, initial_shared_number: Option ) -> anyhow::Result { ... } If set to ​None, it fetches ​initial_shared_number and uses it If set to ​Some(value), it performs no async operation and calls ​self.obj immediately The point of this additional input parameter ​initial_shared_number is that it makes the function intuitive for developers discovering the Rust SDK

Let me know what you think about this! Or if the things coming down the pipe make my proposed solutions irrelevant

8
Comments
.
Thorfin.
Jul 31 2025, 14:12

To fetch shared object versions in the Rust SDK for Sui, you would indeed need to make an RPC call and handle potential errors, as there's no built-in automatic version-fetching like in the TypeScript SDK. However, you can simplify this process by encapsulating the logic in a helper function to minimize the boilerplate code.

Here’s an example of how you can fetch the version of a shared object in Rust and handle the response:

1. Creating the Helper Function:

First, you’ll need to create a function to retrieve the shared object version from the Sui RPC endpoint.

use sui_sdk::rpc_types::{SuiObjectData, ObjectID};
use sui_sdk::SuiClient;
use std::error::Error;
use tokio::runtime::Runtime;

async fn fetch_shared_object_version(client: &SuiClient, object_id: &ObjectID) -> Result<u64, Box<dyn Error>> {
    // Fetch the object data from the Sui client
    let object_data: SuiObjectData = client.read_api().get_object_data(object_id).await?;
    
    // Extract the version of the shared object
    if let Some(version) = object_data.version {
        Ok(version)
    } else {
        Err("No version found for the shared object".into())
    }
}

In this function:

  • We use the SuiClient to query the object’s data.
  • The function returns the version as a u64 if found, or an error if not.

2. Calling the Function:

Then, in your backend logic, you can call this function where you need the shared object version:

fn main() {
    let rt = Runtime::new().unwrap();
    let client = SuiClient::new("http://localhost:5001"); // replace with the correct URL for your Sui client

    let object_id = ObjectID::from_str("your-object-id-here").unwrap();
    
    // Calling the async function in a sync context (main function)
    match rt.block_on(fetch_shared_object_version(&client, &object_id)) {
        Ok(version) => {
            println!("Shared object version: {}", version);
        }
        Err(e) => {
            eprintln!("Error fetching version: {}", e);
        }
    }
}

3. Passing the Shared Object Information:

When you need to pass the shared object to a Move call, you can use the object ID, mutable flag, and version (either fetched or provided manually):

let object_id = ObjectID::from_str("your-object-id-here").unwrap();
let is_mutable = true; // or false based on your logic
let version = 1234567890; // Or the version fetched earlier

// Now you can pass these to your Move call

4. Avoiding Extra Requests:

To avoid making the version fetch on every call, you can cache the version in your backend if the shared object’s version is unlikely to change during your workflow, and only fetch it when necessary (e.g., when the object is mutated).

Key Points:

  • RPC Request: The version fetch does require an RPC call to get_object_data, which could be an extra overhead if done frequently.
  • Cache Versions: Cache the version where possible, especially if the object is static or doesn’t change frequently, to minimize the calls to the blockchain.
  • Manual Version Input: If you already know the version of the object (e.g., from previous interactions), you can manually supply the version, avoiding the need for an additional RPC request.

While this adds an extra step in the Rust SDK, you can streamline it by wrapping the logic in helper functions or implementing caching mechanisms, reducing the developer's burden.

8
Comments
.
Benjamin XDV.
Jul 29 2025, 13:46

Rust SDK Solution

Use get_object to fetch the shared object’s version automatically:

use sui_sdk::SuiClient;

let client = SuiClient::new("https://fullnode.mainnet.sui.io", None).await?;
let shared_obj = client
    .read_api()
    .get_object_with_options(
        "0xSHARED_OBJECT_ID",
        sui_json_rpc_types::ObjectDataOptions::full_content(),
    )
    .await?;

let version = shared_obj.object.version();

PTB Integration

Pass it directly to your transaction:

let mut txb = TransactionBuilder::new();
txb.move_call(
    package_id,
    module,
    function,
    type_args,
    vec![
        Input::shared_object(shared_obj_id, version, /* mutable */ true),
    ],
);

Key Points

Automatic version fetch (like TypeScript SDK)
Optional manual override (pass version if already known)
Error handling built-in (via Result)

4
Comments
.
BigSneh.
Jul 27 2025, 17:41

When working with shared objects in the Sui Rust SDK, developers must provide the object's ID, mutability flag, and its shared version (a u64). The shared version represents the object's initial version when it was first published as shared. This version is required when constructing programmable transaction blocks (PTBs) that interact with shared objects. Unfortunately, unlike the TypeScript SDK, the Rust SDK does not yet automatically fetch the shared version for you. Instead, developers must manually make an RPC call—typically using read_api().get_object()—to retrieve the object and extract the version from the response. This process introduces boilerplate code and increases complexity, especially when handling errors and parsing the results.

In contrast, the TypeScript SDK handles this internally unless a version is explicitly supplied, offering a smoother experience. The Rust SDK could benefit from a utility wrapper or helper method to replicate this functionality. Until then, developers are advised to implement their own abstraction layer to streamline repeated shared object handling. Improving this area would enhance developer productivity and reduce setup time for backend services and transaction builders.

2
Comments
.
290697tz.
Jul 27 2025, 17:44

In the Sui Rust SDK, when constructing programmable transaction blocks (PTBs) that involve shared objects, you must provide three critical components for each shared object: the object ID, its mutability (mutable or immutable), and the shared version represented as a u64. The shared version refers to the version of the object at the point it was marked as shared. This is not always readily known, so you are required to fetch it via an RPC request, typically using the get_object or multi_get_objects method. This creates a poor developer experience because it adds extra complexity to the backend code. You must handle potential RPC failures, missing object states, or deserialization issues.

In contrast, the Sui TypeScript SDK abstracts this away. If a developer does not provide the shared version explicitly, the TypeScript SDK performs the version-fetching logic automatically before building the transaction. This results in a much cleaner development flow. Developers using the Rust SDK have to manually build logic that first fetches the shared version, parses it, handles RPC errors, and only then includes it in the PTB.

This is especially tedious when working with multiple shared objects in batch or automated systems. To simplify things, you could write a wrapper function around the Rust SDK's object fetch API to always extract and return the shared version when needed. This keeps your main PTB construction logic cleaner.

Also, caching previously known shared versions locally can help reduce the number of RPC calls in performance-critical systems. Another workaround is maintaining a mapping of known shared object IDs and their versions if the application logic allows it. However, this requires careful synchronization when network upgrades or object changes occur.

Currently, there is no official support in the Rust SDK for auto-injecting shared versions like in the TypeScript SDK. That might change in future releases as developers demand more ergonomic APIs. Until then, developers building high-level services or tooling on top of the Rust SDK need to plan for this gap. It's a necessary step for accurate PTB execution involving shared objects.

2
Comments
.
Bekky.
Bekky1762
Jul 30 2025, 12:24

Optimal Approach (Rust SDK)

use sui_sdk::{SuiClient, types::object::ObjectRead};

async fn get_shared_version(client: &SuiClient, object_id: &str) -> Result<u64, anyhow::Error> {
    let obj = client
        .read_api()
        .get_object_with_options(
            object_id,
            sui_json_rpc_types::ObjectDataOptions::full_content(),
        )
        .await?;
    Ok(obj.object.version())
}

// Usage in PTB:
let version = get_shared_version(&client, "0xshared_obj_id").await?;
txb.move_call(
    package_id,
    module,
    function,
    vec![Input::shared_object(object_id, version, true)], // true = mutable
);

Key Improvements Over Manual Fetching

  1. Automatic Version Detection - Like TS SDK
  2. Error Handling Included - Propagates Result
  3. Reusable Function - Call anywhere

For Better DX

Wrap this in a trait:

trait SharedObjectExt {
    async fn as_shared_arg(&self, mutable: bool) -> Result<Input, Error>;
}

Why This Works

  • Shared objects always expose their latest version via get_object
  • Rust's type system ensures safety
  • Matches TS SDK behavior while being explicit

Note: Still 1 RPC call per object (unavoidable for freshness). Cache versions if performance-critical.

2
Comments
.
SuiLover.
Jul 27 2025, 17:42

In the Sui Rust SDK, using shared objects in PTBs requires you to provide the object ID, mutability, and its shared version (u64). The shared version is the initial version when the object became shared, and it must be fetched manually using an RPC call like get_object. Unlike the TypeScript SDK, which auto-fetches the shared version if not provided, the Rust SDK puts this responsibility on the developer. This adds extra code and error handling, making development less streamlined. To improve the experience, you can create a helper function to wrap the version-fetching logic.

1
Comments
.

Do you know the answer?

Please log in and share it.