GetObject
Retrieve Object Data with High-Performance gRPC#
The GetObject method from the Ledger Service provides efficient access to Sui blockchain object data through gRPC. This method returns comprehensive object information including metadata, ownership, type information, and contents, with support for field masking to optimize response payloads.
Overview#
Objects are fundamental to Sui's architecture. Every piece of data on Sui exists as an object with a unique identifier, version, ownership model, and type definition. The GetObject gRPC method enables developers to query object state efficiently using binary Protocol Buffer serialization, offering significant performance advantages over JSON-RPC for high-throughput applications.
Key Features#
- Field Masking: Request only necessary fields to minimize response size
- Binary Serialization: Protocol Buffers provide compact, efficient data encoding
- Type Safety: Strong typing through proto definitions eliminates runtime errors
- Version Tracking: Access object version history and previous transactions
- Ownership Information: Detailed ownership metadata including transfer capabilities
Method Signature#
Service: sui.rpc.v2beta2.LedgerService
Method: GetObject
Type: Unary RPC (single request, single response)
Parameters#
| Parameter | Type | Required | Description |
|---|---|---|---|
object_id | string | Yes | The unique identifier of the Sui object (hex string with 0x prefix) |
read_mask | FieldMask | No | Specifies which fields to include in the response |
Field Mask Options#
The read_mask parameter accepts a paths array to specify which fields should be returned. Available paths include:
| Path | Description |
|---|---|
bcs | Binary Canonical Serialization of the object |
object_id | Object's unique identifier |
version | Current version number |
digest | Cryptographic digest of object state |
owner | Ownership information and permissions |
object_type | Type classification (module::struct) |
has_public_transfer | Whether object can be transferred publicly |
contents | Object data fields and values |
previous_transaction | Digest of the transaction that created/modified this version |
storage_rebate | Storage rebate amount in MIST |
Example Object IDs#
0x5 // System object
0x0000000000000000000000000000000000000000000000000000000000000002 // Sui system state
Response Structure#
The method returns an Object message with the following structure:
message Object {
bytes bcs = 1;
string object_id = 2;
uint64 version = 3;
string digest = 4;
Owner owner = 5;
string object_type = 6;
bool has_public_transfer = 7;
ObjectContents contents = 8;
string previous_transaction = 9;
uint64 storage_rebate = 10;
}
Owner Structure#
message Owner {
enum OwnerKind {
ADDRESS_OWNER = 0;
OBJECT_OWNER = 1;
SHARED = 2;
IMMUTABLE = 3;
}
OwnerKind kind = 1;
string address = 2; // Set for ADDRESS_OWNER and OBJECT_OWNER
uint64 initial_shared_version = 3; // Set for SHARED objects
}
Response Fields#
| Field | Type | Description |
|---|---|---|
bcs | bytes | Binary Canonical Serialization data |
object_id | string | Object's unique identifier |
version | uint64 | Current version number |
digest | string | Object state digest |
owner | Owner | Ownership details |
object_type | string | Full type path (package::module::type) |
has_public_transfer | bool | Public transfer capability |
contents | ObjectContents | Structured object data |
previous_transaction | string | Transaction digest that modified this object |
storage_rebate | uint64 | Storage rebate in MIST |
Code Examples#
- TypeScript
- Python
- Go
import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';
// Configuration
const ENDPOINT = 'api-sui-mainnet-full.n.dwellir.com';
const API_TOKEN = 'your_api_token_here';
// Load proto definition
const packageDefinition = protoLoader.loadSync(
'./protos/ledger.proto',
{
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
includeDirs: ['./protos']
}
);
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition) as any;
// Create client with TLS
const credentials = grpc.credentials.createSsl();
const client = new protoDescriptor.sui.rpc.v2beta2.LedgerService(
ENDPOINT,
credentials
);
// Setup authentication
const metadata = new grpc.Metadata();
metadata.add('x-api-key', API_TOKEN);
// Query specific object
async function getObject(objectId: string): Promise<any> {
return new Promise((resolve, reject) => {
const request = {
object_id: objectId,
read_mask: {
paths: [
'object_id',
'version',
'digest',
'owner',
'object_type',
'has_public_transfer',
'previous_transaction',
'storage_rebate'
]
}
};
client.GetObject(request, metadata, (error: any, response: any) => {
if (error) {
console.error('GetObject error:', error.message);
reject(error);
return;
}
resolve(response);
});
});
}
// Example usage
async function main() {
try {
// Query the Sui system state object
const systemObject = await getObject('0x5');
console.log('Object Information:');
console.log('==================');
console.log('Object ID:', systemObject.object_id);
console.log('Version:', systemObject.version);
console.log('Type:', systemObject.object_type);
console.log('Digest:', systemObject.digest);
console.log('Storage Rebate:', systemObject.storage_rebate, 'MIST');
// Parse ownership
if (systemObject.owner) {
const ownerTypes = ['ADDRESS_OWNER', 'OBJECT_OWNER', 'SHARED', 'IMMUTABLE'];
console.log('Owner Type:', ownerTypes[systemObject.owner.kind]);
if (systemObject.owner.address) {
console.log('Owner Address:', systemObject.owner.address);
}
}
console.log('Public Transfer:', systemObject.has_public_transfer ? 'Yes' : 'No');
console.log('Previous Transaction:', systemObject.previous_transaction);
} catch (error) {
console.error('Failed to get object:', error);
}
}
main();
import grpc
import ledger_service_pb2
import ledger_service_pb2_grpc
from google.protobuf import field_mask_pb2
# Configuration
ENDPOINT = 'api-sui-mainnet-full.n.dwellir.com'
API_TOKEN = 'your_api_token_here'
class SuiObjectClient:
def __init__(self, endpoint: str, api_token: str):
self.endpoint = endpoint
self.api_token = api_token
self.channel = None
self.client = None
def connect(self):
"""Establish secure gRPC connection"""
credentials = grpc.ssl_channel_credentials()
self.channel = grpc.secure_channel(self.endpoint, credentials)
self.client = ledger_service_pb2_grpc.LedgerServiceStub(self.channel)
def get_object(self, object_id: str, fields=None):
"""
Retrieve object information
Args:
object_id: Sui object ID (hex string)
fields: List of field paths to retrieve (None = all fields)
Returns:
Object response message
"""
if not self.client:
self.connect()
# Setup authentication
metadata = [('x-api-key', self.api_token)]
# Default fields if none specified
if fields is None:
fields = [
'object_id',
'version',
'digest',
'owner',
'object_type',
'has_public_transfer',
'previous_transaction',
'storage_rebate'
]
# Build request with field mask
request = ledger_service_pb2.GetObjectRequest(
object_id=object_id,
read_mask=field_mask_pb2.FieldMask(paths=fields)
)
try:
response = self.client.GetObject(request, metadata=metadata, timeout=10.0)
return response
except grpc.RpcError as e:
print(f'gRPC Error: {e.code()}: {e.details()}')
raise
def close(self):
"""Close gRPC channel"""
if self.channel:
self.channel.close()
# Example usage
def main():
client = SuiObjectClient(ENDPOINT, API_TOKEN)
try:
# Query Sui system state object
obj = client.get_object('0x5')
print('Object Information:')
print('==================')
print(f'Object ID: {obj.object_id}')
print(f'Version: {obj.version}')
print(f'Type: {obj.object_type}')
print(f'Digest: {obj.digest}')
print(f'Storage Rebate: {obj.storage_rebate} MIST')
# Parse owner information
owner_types = ['ADDRESS_OWNER', 'OBJECT_OWNER', 'SHARED', 'IMMUTABLE']
print(f'Owner Type: {owner_types[obj.owner.kind]}')
if obj.owner.address:
print(f'Owner Address: {obj.owner.address}')
print(f'Public Transfer: {"Yes" if obj.has_public_transfer else "No"}')
print(f'Previous Transaction: {obj.previous_transaction}')
except Exception as e:
print(f'Error: {e}')
finally:
client.close()
if __name__ == '__main__':
main()
package main
import (
"context"
"fmt"
"log"
"time"
"sui-grpc-client/config"
pb "sui-grpc-client/sui/rpc/v2"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
"google.golang.org/protobuf/types/known/fieldmaskpb"
)
type SuiObjectClient struct {
conn *grpc.ClientConn
client pb.LedgerServiceClient
cfg *config.Config
}
func NewSuiObjectClient(cfg *config.Config) (*SuiObjectClient, error) {
// Create TLS credentials
creds := credentials.NewClientTLSFromCert(nil, "")
// Establish connection
conn, err := grpc.NewClient(
cfg.Endpoint,
grpc.WithTransportCredentials(creds),
)
if err != nil {
return nil, fmt.Errorf("failed to connect: %w", err)
}
// Create client
client := pb.NewLedgerServiceClient(conn)
return &SuiObjectClient{
conn: conn,
client: client,
cfg: cfg,
}, nil
}
func (c *SuiObjectClient) GetObject(ctx context.Context, objectID string, fields []string) (*pb.Object, error) {
// Add authentication metadata
ctx = metadata.AppendToOutgoingContext(ctx, "x-api-key", c.cfg.APIKey)
// Default fields if none specified
if len(fields) == 0 {
fields = []string{
"object_id",
"version",
"digest",
"owner",
"object_type",
"has_public_transfer",
"previous_transaction",
"storage_rebate",
}
}
// Build request
objID := objectID
request := &pb.GetObjectRequest{
ObjectId: &objID,
ReadMask: &fieldmaskpb.FieldMask{
Paths: fields,
},
}
// Execute RPC
response, err := c.client.GetObject(ctx, request)
if err != nil {
return nil, fmt.Errorf("GetObject RPC failed: %w", err)
}
return response.GetObject(), nil
}
func (c *SuiObjectClient) Close() error {
return c.conn.Close()
}
func main() {
// Load configuration from .env file
cfg, err := config.Load()
if err != nil {
log.Fatalf("Failed to load config: %v", err)
}
// Create client
client, err := NewSuiObjectClient(cfg)
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
defer client.Close()
// Create context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Query Sui system state object
obj, err := client.GetObject(ctx, "0x5", nil)
if err != nil {
log.Fatalf("Failed to get object: %v", err)
}
// Display results
fmt.Println("Object Information:")
fmt.Println("==================")
fmt.Printf("Object ID: %s\n", obj.GetObjectId())
fmt.Printf("Version: %d\n", obj.GetVersion())
fmt.Printf("Type: %s\n", obj.GetObjectType())
fmt.Printf("Digest: %s\n", obj.GetDigest())
fmt.Printf("Storage Rebate: %d MIST\n", obj.GetStorageRebate())
// Parse owner
if owner := obj.GetOwner(); owner != nil {
ownerKind := owner.GetKind()
switch ownerKind {
case pb.Owner_ADDRESS:
fmt.Printf("Owner Type: ADDRESS\n")
fmt.Printf("Owner Address: %s\n", owner.GetAddress())
case pb.Owner_OBJECT:
fmt.Printf("Owner Type: OBJECT\n")
fmt.Printf("Owner Object: %s\n", owner.GetAddress())
case pb.Owner_SHARED:
fmt.Printf("Owner Type: SHARED\n")
fmt.Printf("Initial Shared Version: %d\n", owner.GetVersion())
case pb.Owner_IMMUTABLE:
fmt.Printf("Owner Type: IMMUTABLE\n")
}
}
fmt.Printf("Public Transfer: %t\n", obj.GetHasPublicTransfer())
fmt.Printf("Previous Transaction: %s\n", obj.GetPreviousTransaction())
}
Use Cases#
1. Object Ownership Verification#
Verify ownership before performing operations:
async function verifyOwnership(objectId: string, expectedOwner: string): Promise<boolean> {
const object = await getObject(objectId);
if (object.owner.kind === 0) { // ADDRESS_OWNER
return object.owner.address === expectedOwner;
}
return false;
}
2. Object Version Tracking#
Monitor object version changes:
async function trackObjectVersions(objectId: string): Promise<void> {
const object = await getObject(objectId);
console.log('Current Version:', object.version);
console.log('Last Modified By:', object.previous_transaction);
// Store version for comparison
localStorage.setItem(`${objectId}_version`, object.version);
}
3. NFT Metadata Retrieval#
Efficiently query NFT objects:
async function getNFTMetadata(nftObjectId: string) {
const nft = await client.GetObject({
object_id: nftObjectId,
read_mask: {
paths: [
'object_id',
'object_type',
'owner',
'contents',
'has_public_transfer'
]
}
}, metadata);
return {
id: nft.object_id,
type: nft.object_type,
owner: nft.owner.address,
transferrable: nft.has_public_transfer,
metadata: nft.contents
};
}
4. Storage Cost Analysis#
Analyze storage costs across objects:
async function analyzeStorageCosts(objectIds: string[]): Promise<void> {
let totalRebate = 0;
for (const id of objectIds) {
const obj = await client.GetObject({
object_id: id,
read_mask: { paths: ['storage_rebate'] }
}, metadata);
totalRebate += parseInt(obj.storage_rebate);
}
console.log(`Total Storage Rebate: ${totalRebate / 1_000_000_000} SUI`);
}
Field Masking for Performance#
Field masking dramatically reduces response size and improves performance:
Full Response (No Field Mask)#
const request = { object_id: '0x5' };
// Response size: ~2.5 KB
Optimized Response (With Field Mask)#
const request = {
object_id: '0x5',
read_mask: {
paths: ['object_id', 'version', 'owner']
}
};
// Response size: ~150 bytes (94% reduction)
Performance Impact#
| Fields Requested | Response Size | Parsing Time | Bandwidth Saved |
|---|---|---|---|
| All (no mask) | 2.5 KB | 1.2 ms | 0% |
| 8 fields | 800 bytes | 0.4 ms | 68% |
| 3 fields | 150 bytes | 0.1 ms | 94% |
Error Handling#
Handle common error scenarios:
async function safeGetObject(objectId: string) {
try {
const object = await getObject(objectId);
return { success: true, data: object };
} catch (error: any) {
switch (error.code) {
case grpc.status.NOT_FOUND:
return {
success: false,
error: 'Object not found. It may have been deleted or never existed.'
};
case grpc.status.INVALID_ARGUMENT:
return {
success: false,
error: 'Invalid object ID format. Ensure it\'s a valid hex string.'
};
case grpc.status.PERMISSION_DENIED:
return {
success: false,
error: 'Authentication failed. Check your API token.'
};
case grpc.status.DEADLINE_EXCEEDED:
return {
success: false,
error: 'Request timeout. The server took too long to respond.'
};
default:
return {
success: false,
error: `Unexpected error: ${error.message}`
};
}
}
}
Best Practices#
1. Use Field Masking#
Always specify only the fields you need:
// ✅ Good: Request only needed fields
const request = {
object_id: objectId,
read_mask: { paths: ['owner', 'version'] }
};
// ❌ Bad: Request all fields when you only need a few
const request = { object_id: objectId };
2. Implement Caching#
Cache object data to reduce API calls:
const objectCache = new Map();
async function getCachedObject(objectId: string, maxAge: number = 60000) {
const cached = objectCache.get(objectId);
if (cached && Date.now() - cached.timestamp < maxAge) {
return cached.data;
}
const object = await getObject(objectId);
objectCache.set(objectId, {
data: object,
timestamp: Date.now()
});
return object;
}
3. Handle Version Conflicts#
Check version numbers to avoid stale data:
async function updateObjectIfNewer(objectId: string, lastKnownVersion: number) {
const object = await client.GetObject({
object_id: objectId,
read_mask: { paths: ['version', 'digest'] }
}, metadata);
if (object.version > lastKnownVersion) {
console.log('Object updated:', {
oldVersion: lastKnownVersion,
newVersion: object.version,
digest: object.digest
});
return object;
}
return null; // No update needed
}
4. Batch Queries#
For multiple objects, use BatchGetObjects instead:
// ✅ Efficient: Single batch request
const response = await client.BatchGetObjects({
requests: objectIds.map(id => ({ object_id: id }))
}, metadata);
// ❌ Inefficient: Multiple individual requests
for (const id of objectIds) {
await client.GetObject({ object_id: id }, metadata);
}
Related Methods#
- BatchGetObjects - Retrieve multiple objects efficiently
- GetTransaction - Query transaction details
- ListOwnedObjects - List objects owned by an address
Performance Considerations#
- Field Masking: Reduces response size by up to 94%
- Binary Serialization: 5-10x faster than JSON parsing
- Connection Reuse: Keep gRPC channel open for multiple requests
- Compression: Enable gRPC compression for large payloads
- Caching: Cache immutable objects to avoid repeated queries
Need help with gRPC integration? Contact our support team or check the gRPC overview.