Skip to main content

GetDatatype

Query Move Struct and Type Definitions#

The GetDatatype method retrieves detailed information about Move structs and datatypes, including their fields, type parameters, and abilities. This is essential for understanding smart contract data structures, building transaction inputs, and parsing event data.

Method Signature#

Service: sui.rpc.v2beta2.MovePackageService Method: GetDatatype Type: Unary RPC

Parameters#

ParameterTypeRequiredDescription
package_idstringYesPackage object ID
module_namestringYesModule name within package
datatype_namestringYesStruct/type name within module

Response Structure#

message MoveStruct {
string name = 1;
repeated MoveAbility abilities = 2;
repeated MoveTypeParameter type_parameters = 3;
repeated MoveField fields = 4;
}

message MoveField {
string name = 1;
MoveType type = 2;
}

enum MoveAbility {
COPY = 0;
DROP = 1;
STORE = 2;
KEY = 3;
}

Code Examples#

import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';

const ENDPOINT = 'api-sui-mainnet-full.n.dwellir.com';
const API_TOKEN = 'your_api_token_here';

const packageDefinition = protoLoader.loadSync('./protos/package.proto', {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
includeDirs: ['./protos']
});

const protoDescriptor = grpc.loadPackageDefinition(packageDefinition) as any;
const credentials = grpc.credentials.createSsl();
const client = new protoDescriptor.sui.rpc.v2beta2.MovePackageService(ENDPOINT, credentials);

const metadata = new grpc.Metadata();
metadata.add('x-api-key', API_TOKEN);

async function getDatatype(
packageId: string,
moduleName: string,
datatypeName: string
): Promise<any> {
return new Promise((resolve, reject) => {
const request = {
package_id: packageId,
module_name: moduleName,
datatype_name: datatypeName
};

client.GetDatatype(request, metadata, (error: any, response: any) => {
if (error) {
console.error('GetDatatype error:', error.message);
reject(error);
return;
}

resolve(response);
});
});
}

// Usage - Query Coin struct
const coinStruct = await getDatatype('0x2', 'coin', 'Coin');

console.log('Struct:', coinStruct.name);
console.log('Abilities:', coinStruct.abilities);
console.log('Type Parameters:', coinStruct.type_parameters.length);
console.log('Fields:');
coinStruct.fields.forEach((field: any) => {
console.log(` ${field.name}: ${field.type}`);
});

Use Cases#

Generate TypeScript Interface#

async function generateTypeScriptInterface(
packageId: string,
moduleName: string,
datatypeName: string
): Promise<string> {
const struct = await getDatatype(packageId, moduleName, datatypeName);

const typeParams = struct.type_parameters?.length > 0
? `<${struct.type_parameters.map((t: any) => t.name).join(', ')}>`
: '';

const fields = struct.fields
.map((field: any) => ` ${field.name}: ${mapMoveTypeToTS(field.type)};`)
.join('\n');

return `
interface ${struct.name}${typeParams} {
${fields}
}
`;
}

function mapMoveTypeToTS(moveType: string): string {
// Simplified mapping
if (moveType.includes('u64') || moveType.includes('u128')) return 'bigint';
if (moveType.includes('u8') || moveType.includes('u16') || moveType.includes('u32')) return 'number';
if (moveType.includes('bool')) return 'boolean';
if (moveType.includes('address')) return 'string';
if (moveType.includes('vector')) return 'any[]';
return 'any';
}

// Usage
const tsInterface = await generateTypeScriptInterface('0x2', 'coin', 'Coin');
console.log(tsInterface);

Validate Object Structure#

interface ValidationResult {
isValid: boolean;
missingFields: string[];
extraFields: string[];
}

async function validateObjectStructure(
packageId: string,
moduleName: string,
datatypeName: string,
objectData: any
): Promise<ValidationResult> {
const struct = await getDatatype(packageId, moduleName, datatypeName);

const expectedFields = new Set(struct.fields.map((f: any) => f.name));
const actualFields = new Set(Object.keys(objectData));

const missingFields = Array.from(expectedFields).filter(f => !actualFields.has(f));
const extraFields = Array.from(actualFields).filter(f => !expectedFields.has(f));

return {
isValid: missingFields.length === 0 && extraFields.length === 0,
missingFields,
extraFields
};
}

// Usage
const validation = await validateObjectStructure(
'0x2',
'coin',
'Coin',
{ id: '0x...', balance: '1000', value: '1000' }
);

if (!validation.isValid) {
console.error('Invalid structure:');
if (validation.missingFields.length > 0) {
console.error(' Missing:', validation.missingFields);
}
if (validation.extraFields.length > 0) {
console.error(' Extra:', validation.extraFields);
}
}

Build Struct Documentation#

interface StructDocs {
name: string;
fullName: string;
abilities: string[];
typeParameters: string[];
fields: Array<{
name: string;
type: string;
}>;
description: string;
}

async function generateStructDocs(
packageId: string,
moduleName: string,
datatypeName: string
): Promise<StructDocs> {
const struct = await getDatatype(packageId, moduleName, datatypeName);

return {
name: struct.name,
fullName: `${packageId}::${moduleName}::${struct.name}`,
abilities: struct.abilities || [],
typeParameters: struct.type_parameters?.map((t: any) => t.name) || [],
fields: struct.fields.map((f: any) => ({
name: f.name,
type: f.type.toString()
})),
description: `Struct defined in ${moduleName} module`
};
}

// Usage
const docs = await generateStructDocs('0x2', 'coin', 'Coin');

console.log(`# ${docs.name}\n`);
console.log(`**Full Type:** \`${docs.fullName}\`\n`);
console.log(`**Abilities:** ${docs.abilities.join(', ')}\n`);
if (docs.typeParameters.length > 0) {
console.log(`**Type Parameters:** ${docs.typeParameters.join(', ')}\n`);
}
console.log(`## Fields\n`);
docs.fields.forEach(field => {
console.log(`- \`${field.name}\`: ${field.type}`);
});

Parse Event Data#

async function parseEventData(
eventType: string,
eventData: any
): Promise<any> {
// Parse type string: package::module::Type
const [packageId, moduleName, datatypeName] = eventType.split('::');

const struct = await getDatatype(packageId, moduleName, datatypeName);

const parsed: any = {};

struct.fields.forEach((field: any, index: number) => {
const value = eventData[field.name] || eventData[index];
parsed[field.name] = value;
});

return parsed;
}

// Usage
const event = {
type: '0x2::coin::DepositEvent',
data: { amount: '1000000', sender: '0x...' }
};

const parsedEvent = await parseEventData(event.type, event.data);
console.log('Parsed event:', parsedEvent);

Check Struct Abilities#

async function checkAbilities(
packageId: string,
moduleName: string,
datatypeName: string
): Promise<{
canCopy: boolean;
canDrop: boolean;
canStore: boolean;
hasKey: boolean;
}> {
const struct = await getDatatype(packageId, moduleName, datatypeName);

const abilities = struct.abilities || [];

return {
canCopy: abilities.includes('COPY'),
canDrop: abilities.includes('DROP'),
canStore: abilities.includes('STORE'),
hasKey: abilities.includes('KEY')
};
}

// Usage
const abilities = await checkAbilities('0x2', 'coin', 'Coin');

console.log('Coin abilities:');
console.log(' Can copy:', abilities.canCopy);
console.log(' Can drop:', abilities.canDrop);
console.log(' Can store:', abilities.canStore);
console.log(' Has key:', abilities.hasKey);

Build Object Schema#

interface ObjectSchema {
type: string;
fields: Map<string, {
type: string;
required: boolean;
}>;
}

async function buildObjectSchema(
packageId: string,
moduleName: string,
datatypeName: string
): Promise<ObjectSchema> {
const struct = await getDatatype(packageId, moduleName, datatypeName);

const fields = new Map();

struct.fields.forEach((field: any) => {
fields.set(field.name, {
type: field.type.toString(),
required: true // All fields are required in Move structs
});
});

return {
type: `${packageId}::${moduleName}::${struct.name}`,
fields
};
}

// Usage
const schema = await buildObjectSchema('0x2', 'coin', 'Coin');

console.log('Schema for', schema.type);
for (const [fieldName, fieldInfo] of schema.fields) {
console.log(` ${fieldName}: ${fieldInfo.type} (required: ${fieldInfo.required})`);
}

Best Practices#

Cache Struct Definitions#

class DatatypeCache {
private cache = new Map<string, { data: any; timestamp: number }>();
private ttl = 3600000; // 1 hour

async getDatatype(
packageId: string,
moduleName: string,
datatypeName: string
): Promise<any> {
const key = `${packageId}::${moduleName}::${datatypeName}`;
const cached = this.cache.get(key);

if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.data;
}

const datatype = await getDatatype(packageId, moduleName, datatypeName);

this.cache.set(key, {
data: datatype,
timestamp: Date.now()
});

return datatype;
}
}

Handle Generic Types#

async function getGenericStructInfo(
packageId: string,
moduleName: string,
datatypeName: string
): Promise<{
name: string;
isGeneric: boolean;
typeParameters: string[];
}> {
const struct = await getDatatype(packageId, moduleName, datatypeName);

return {
name: struct.name,
isGeneric: struct.type_parameters && struct.type_parameters.length > 0,
typeParameters: struct.type_parameters?.map((t: any) => t.name) || []
};
}

Performance Characteristics#

MetricValue
Typical Latency15-40ms
Response Size500-5000 bytes
Cache RecommendedYes (long TTL)
Rate Limit ImpactLow

Common Sui Framework Types#

TypePackage::ModuleDescription
Coin0x2::coin::CoinGeneric coin type
Object0x2::object::UIDObject identifier
Balance0x2::balance::BalanceGeneric balance type
TxContext0x2::tx_context::TxContextTransaction context

Common Errors#

Error CodeScenarioSolution
NOT_FOUNDStruct doesn't existVerify package, module, and struct names
INVALID_ARGUMENTInvalid namesCheck naming format
UNAUTHENTICATEDMissing/invalid tokenVerify x-api-key header

Need help? Contact support@dwellir.com or check the gRPC overview.