Skip to main content

suix_getDynamicFields

Returns the list of dynamic field objects owned by an object on the Sui network.

Overview​

The suix_getDynamicFields method allows you to query dynamic fields attached to any Sui object. Dynamic fields provide a way to add heterogeneous data to objects after creation, enabling flexible data structures and extensible object designs. This method is essential for exploring complex object hierarchies and understanding object relationships in Sui applications.

Parameters​

ParameterTypeRequiredDescription
parentIdstringYesThe ID of the parent object to query for dynamic fields
cursorstringNoOptional paging cursor for large result sets
limitnumberNoMaximum number of items to return (default: 50, max: 50)

Object ID Format​

  • Object IDs are 66-character hex strings with 0x prefix
  • Must be a valid Sui object ID
  • The parent object must exist and be accessible

Returns​

Returns a paginated list of dynamic field information.

FieldTypeDescription
dataarrayArray of dynamic field objects
nextCursorstringCursor for the next page of results (if applicable)
hasNextPagebooleanWhether there are more results available

Dynamic Field Object Structure​

Each dynamic field object contains:

FieldTypeDescription
nameobjectThe field name (type and value)
bcsNamestringBCS-encoded field name
typestringThe type of the dynamic field
objectTypestringThe type of the field value
objectIdstringThe object ID of the field value
versionstringObject version
digeststringObject digest

Code Examples​

# Get dynamic fields for an object
curl -X POST https://sui-mainnet.dwellir.com/YOUR_API_KEY \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "suix_getDynamicFields",
"params": [
"0xd77955e670601c2c2e6e8637e383695c166aac0a86b741c266bdfb23c2e3369f"
],
"id": 1
}'

# Get dynamic fields with pagination
curl -X POST https://sui-mainnet.dwellir.com/YOUR_API_KEY \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "suix_getDynamicFields",
"params": [
"0xd77955e670601c2c2e6e8637e383695c166aac0a86b741c266bdfb23c2e3369f",
null,
10
],
"id": 1
}'

Response Example​

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"data": [
{
"name": {
"type": "0x2::string::String",
"value": "user_profile"
},
"bcsName": "0x757365725f70726f66696c65",
"type": "DynamicField",
"objectType": "0x2::dynamic_field::Field<0x2::string::String, 0xa1b2c3::user::Profile>",
"objectId": "0x1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"version": "12345",
"digest": "AbCdEf123456789..."
},
{
"name": {
"type": "u64",
"value": "42"
},
"bcsName": "0x2a",
"type": "DynamicField",
"objectType": "0x2::dynamic_field::Field<u64, vector<u8>>",
"objectId": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
"version": "67890",
"digest": "123456AbCdEf..."
}
],
"nextCursor": "0x9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba",
"hasNextPage": true
}
}

Practical Examples​

Dynamic Field Manager​

class DynamicFieldManager {
constructor(client) {
this.client = client;
this.fieldCache = new Map();
}

async getFieldsStructure(parentId, useCache = true) {
const cacheKey = `fields_${parentId}`;

if (useCache && this.fieldCache.has(cacheKey)) {
return this.fieldCache.get(cacheKey);
}

const fields = await this.getAllDynamicFields(parentId);

const structure = {
parentId: parentId,
totalFields: fields.length,
fieldsByType: this.groupFieldsByType(fields),
fieldsByName: this.groupFieldsByName(fields),
metadata: {
lastUpdated: Date.now(),
versions: fields.map(f => parseInt(f.version))
}
};

if (useCache) {
this.fieldCache.set(cacheKey, structure);
}

return structure;
}

groupFieldsByType(fields) {
return fields.reduce((acc, field) => {
const type = field.objectType;
if (!acc[type]) {
acc[type] = [];
}
acc[type].push(field);
return acc;
}, {});
}

groupFieldsByName(fields) {
return fields.reduce((acc, field) => {
const nameKey = this.getFieldNameKey(field.name);
acc[nameKey] = field;
return acc;
}, {});
}

getFieldNameKey(name) {
if (typeof name === 'string') return name;
if (name && name.value) return name.value.toString();
return JSON.stringify(name);
}

async getAllDynamicFields(parentId) {
let allFields = [];
let cursor = null;
let hasNextPage = true;

while (hasNextPage) {
const result = await this.client.getDynamicFields({
parentId: parentId,
cursor: cursor,
limit: 50
});

allFields = allFields.concat(result.data);
cursor = result.nextCursor;
hasNextPage = result.hasNextPage;
}

return allFields;
}
}

Field Value Resolver​

class FieldValueResolver {
constructor(client) {
this.client = client;
this.valueCache = new Map();
}

async resolveFieldValues(parentId, fieldNames = []) {
const fields = await this.client.getDynamicFields({ parentId });
const resolved = {};

for (const field of fields.data) {
const nameKey = this.getFieldNameKey(field.name);

// If specific fields requested, filter
if (fieldNames.length > 0 && !fieldNames.includes(nameKey)) {
continue;
}

try {
const value = await this.getFieldValue(field.objectId);
resolved[nameKey] = {
field: field,
value: value,
resolved: true
};
} catch (error) {
resolved[nameKey] = {
field: field,
error: error.message,
resolved: false
};
}
}

return resolved;
}

async getFieldValue(fieldObjectId, useCache = true) {
if (useCache && this.valueCache.has(fieldObjectId)) {
return this.valueCache.get(fieldObjectId);
}

const fieldObject = await this.client.getObject({
id: fieldObjectId,
options: {
showType: true,
showContent: true,
showBcs: true
}
});

if (fieldObject && fieldObject.data) {
if (useCache) {
this.valueCache.set(fieldObjectId, fieldObject.data);
}
return fieldObject.data;
}

return null;
}

getFieldNameKey(name) {
if (typeof name === 'string') return name;
if (name && name.value !== undefined) return name.value.toString();
return JSON.stringify(name);
}
}

Dynamic Object Inspector​

class DynamicObjectInspector {
constructor(client) {
this.client = client;
}

async inspectObject(objectId, options = {}) {
const {
includeFieldValues = false,
maxFieldsToInspect = 20,
includeTypeAnalysis = true
} = options;

try {
// Get object info
const objectInfo = await this.client.getObject({
id: objectId,
options: { showType: true, showContent: true }
});

// Get dynamic fields
const fieldsResult = await this.client.getDynamicFields({
parentId: objectId,
limit: maxFieldsToInspect
});

const inspection = {
objectId: objectId,
objectType: objectInfo.data?.type,
objectVersion: objectInfo.data?.version,
dynamicFieldsCount: fieldsResult.data.length,
hasMoreFields: fieldsResult.hasNextPage,
fields: fieldsResult.data
};

if (includeTypeAnalysis) {
inspection.typeAnalysis = this.analyzeFieldTypes(fieldsResult.data);
}

if (includeFieldValues) {
inspection.fieldValues = {};

for (const field of fieldsResult.data.slice(0, 10)) { // Limit to first 10
try {
const value = await this.client.getObject({
id: field.objectId,
options: { showContent: true }
});

const nameKey = this.getFieldNameKey(field.name);
inspection.fieldValues[nameKey] = value.data;
} catch (error) {
console.warn(`Failed to get value for field ${field.objectId}:`, error);
}
}
}

return inspection;

} catch (error) {
return {
objectId: objectId,
error: error.message,
timestamp: Date.now()
};
}
}

analyzeFieldTypes(fields) {
const typeCount = {};
const nameTypes = {};

fields.forEach(field => {
// Count object types
const objType = field.objectType;
typeCount[objType] = (typeCount[objType] || 0) + 1;

// Analyze name types
const nameType = typeof field.name === 'object' ?
field.name.type || 'object' : typeof field.name;
nameTypes[nameType] = (nameTypes[nameType] || 0) + 1;
});

return {
objectTypes: typeCount,
nameTypes: nameTypes,
totalFields: fields.length,
uniqueTypes: Object.keys(typeCount).length
};
}

getFieldNameKey(name) {
if (typeof name === 'string') return name;
if (name && name.value !== undefined) return name.value.toString();
return JSON.stringify(name);
}

async compareObjects(objectIds) {
const comparisons = [];

for (const objectId of objectIds) {
const inspection = await this.inspectObject(objectId, {
includeTypeAnalysis: true,
includeFieldValues: false
});
comparisons.push(inspection);
}

return {
objects: comparisons,
comparison: {
totalObjects: comparisons.length,
averageFieldsPerObject: comparisons.reduce(
(sum, obj) => sum + (obj.dynamicFieldsCount || 0), 0
) / comparisons.length,
commonTypes: this.findCommonTypes(comparisons),
uniquePatterns: this.findUniquePatterns(comparisons)
}
};
}

findCommonTypes(comparisons) {
const allTypes = new Set();
comparisons.forEach(comp => {
if (comp.typeAnalysis) {
Object.keys(comp.typeAnalysis.objectTypes).forEach(type =>
allTypes.add(type)
);
}
});

const commonTypes = {};
allTypes.forEach(type => {
const count = comparisons.filter(comp =>
comp.typeAnalysis?.objectTypes[type] > 0
).length;

if (count > 1) {
commonTypes[type] = count;
}
});

return commonTypes;
}

findUniquePatterns(comparisons) {
const patterns = [];

comparisons.forEach(comp => {
if (comp.typeAnalysis) {
const uniqueTypes = Object.keys(comp.typeAnalysis.objectTypes).filter(
type => {
const appearsInOthers = comparisons.some(other =>
other !== comp && other.typeAnalysis?.objectTypes[type] > 0
);
return !appearsInOthers;
}
);

if (uniqueTypes.length > 0) {
patterns.push({
objectId: comp.objectId,
uniqueTypes: uniqueTypes
});
}
}
});

return patterns;
}
}

Error Handling​

Common errors and how to handle them:

async function safeDynamicFieldsQuery(parentId, options = {}) {
const { maxRetries = 3, retryDelay = 1000 } = options;

for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const result = await client.getDynamicFields({
parentId: parentId,
limit: 50
});

if (!result || !result.data) {
throw new Error('Invalid response format');
}

return { success: true, data: result };

} catch (error) {
console.warn(`Dynamic fields query attempt ${attempt} failed:`, error.message);

// Handle specific error types
if (error.message.includes('Object not found')) {
return {
success: false,
error: 'OBJECT_NOT_FOUND',
message: 'Parent object does not exist or is not accessible'
};
}

if (error.message.includes('Invalid object ID')) {
return {
success: false,
error: 'INVALID_OBJECT_ID',
message: 'Invalid object ID format'
};
}

if (attempt === maxRetries) {
return {
success: false,
error: 'MAX_RETRIES_EXCEEDED',
message: error.message
};
}

await new Promise(resolve => setTimeout(resolve, retryDelay * attempt));
}
}
}

Best Practices​

  1. Use Pagination: Always handle pagination for objects with many dynamic fields
  2. Cache Results: Cache field information when exploring object hierarchies
  3. Handle Large Objects: Be mindful of objects with hundreds of dynamic fields
  4. Validate Field Names: Handle different field name formats (strings, objects, numbers)
  5. Implement Retries: Network requests can fail, implement retry logic
  6. Limit Depth: When exploring recursively, set reasonable depth limits

Need help? Contact our support team or check the Sui documentation.