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#
| Parameter | Type | Required | Description |
|---|---|---|---|
package_id | string | Yes | Package object ID |
module_name | string | Yes | Module name within package |
datatype_name | string | Yes | Struct/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#
- TypeScript
- Python
- Go
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}`);
});
import grpc
import package_service_pb2
import package_service_pb2_grpc
ENDPOINT = 'api-sui-mainnet-full.n.dwellir.com'
API_TOKEN = 'your_api_token_here'
def get_datatype(package_id: str, module_name: str, datatype_name: str):
credentials = grpc.ssl_channel_credentials()
channel = grpc.secure_channel(ENDPOINT, credentials)
client = package_service_pb2_grpc.MovePackageServiceStub(channel)
request = package_service_pb2.GetDatatypeRequest(
package_id=package_id,
module_name=module_name,
datatype_name=datatype_name
)
metadata = [('x-api-key', API_TOKEN)]
response = client.GetDatatype(request, metadata=metadata)
print(f'Struct: {response.name}')
print(f'Abilities: {response.abilities}')
print('Fields:')
for field in response.fields:
print(f' {field.name}: {field.type}')
channel.close()
return response
# Usage
coin_struct = get_datatype('0x2', 'coin', 'Coin')
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"
)
func main() {
// Load configuration from .env file
cfg, err := config.Load()
if err != nil {
log.Fatalf("Failed to load config: %v", err)
}
// Create TLS credentials
creds := credentials.NewClientTLSFromCert(nil, "")
// Connect to Dwellir
conn, err := grpc.NewClient(
cfg.Endpoint,
grpc.WithTransportCredentials(creds),
)
if err != nil {
log.Fatalf("Failed to connect: %v", err)
}
defer conn.Close()
// Create move package service client
client := pb.NewMovePackageServiceClient(conn)
// Add authentication
ctx := metadata.AppendToOutgoingContext(
context.Background(),
"x-api-key", cfg.APIKey,
)
// Set timeout
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
// Example: Get the Coin datatype from the Sui Framework (0x2::coin::Coin)
packageId := "0x0000000000000000000000000000000000000000000000000000000000000002"
moduleName := "coin"
datatypeName := "Coin"
request := &pb.GetDatatypeRequest{
PackageId: &packageId,
ModuleName: &moduleName,
Name: &datatypeName,
}
response, err := client.GetDatatype(ctx, request)
if err != nil {
log.Fatalf("Failed to get datatype: %v", err)
}
datatype := response.GetDatatype()
if datatype == nil {
log.Fatal("No datatype data returned")
}
// Display datatype information
fmt.Println("Datatype Information:")
fmt.Println("=====================")
fmt.Printf("Type Name: %s\n", datatype.GetTypeName())
fmt.Printf("Defining Package ID: %s\n", datatype.GetDefiningId())
fmt.Printf("Module: %s\n", datatype.GetModule())
fmt.Printf("Name: %s\n", datatype.GetName())
// Display kind (STRUCT or ENUM)
kind := datatype.GetKind()
switch kind {
case pb.DatatypeDescriptor_STRUCT:
fmt.Printf("Kind: STRUCT\n")
case pb.DatatypeDescriptor_ENUM:
fmt.Printf("Kind: ENUM\n")
default:
fmt.Printf("Kind: UNKNOWN\n")
}
// Display abilities
abilities := datatype.GetAbilities()
if len(abilities) > 0 {
fmt.Println("\nAbilities:")
for _, ability := range abilities {
switch ability {
case pb.Ability_COPY:
fmt.Println(" - COPY")
case pb.Ability_DROP:
fmt.Println(" - DROP")
case pb.Ability_STORE:
fmt.Println(" - STORE")
case pb.Ability_KEY:
fmt.Println(" - KEY")
}
}
}
// Display type parameters
typeParams := datatype.GetTypeParameters()
if len(typeParams) > 0 {
fmt.Printf("\nType Parameters: %d\n", len(typeParams))
for i, param := range typeParams {
fmt.Printf(" Parameter %d:\n", i+1)
fmt.Printf(" Is Phantom: %v\n", param.GetIsPhantom())
if len(param.GetConstraints()) > 0 {
fmt.Printf(" Constraints: ")
for j, constraint := range param.GetConstraints() {
if j > 0 {
fmt.Printf(", ")
}
switch constraint {
case pb.Ability_COPY:
fmt.Printf("COPY")
case pb.Ability_DROP:
fmt.Printf("DROP")
case pb.Ability_STORE:
fmt.Printf("STORE")
case pb.Ability_KEY:
fmt.Printf("KEY")
}
}
fmt.Println()
}
}
}
// Display fields if it's a struct
fields := datatype.GetFields()
if len(fields) > 0 {
fmt.Printf("\nFields: %d\n", len(fields))
for _, field := range fields {
fmt.Printf(" %d. %s\n", field.GetPosition(), field.GetName())
}
}
// Display variants if it's an enum
variants := datatype.GetVariants()
if len(variants) > 0 {
fmt.Printf("\nVariants: %d\n", len(variants))
for _, variant := range variants {
fmt.Printf(" %d. %s\n", variant.GetPosition(), variant.GetName())
}
}
}
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#
| Metric | Value |
|---|---|
| Typical Latency | 15-40ms |
| Response Size | 500-5000 bytes |
| Cache Recommended | Yes (long TTL) |
| Rate Limit Impact | Low |
Common Sui Framework Types#
| Type | Package::Module | Description |
|---|---|---|
| Coin | 0x2::coin::Coin | Generic coin type |
| Object | 0x2::object::UID | Object identifier |
| Balance | 0x2::balance::Balance | Generic balance type |
| TxContext | 0x2::tx_context::TxContext | Transaction context |
Common Errors#
| Error Code | Scenario | Solution |
|---|---|---|
NOT_FOUND | Struct doesn't exist | Verify package, module, and struct names |
INVALID_ARGUMENT | Invalid names | Check naming format |
UNAUTHENTICATED | Missing/invalid token | Verify x-api-key header |
Related Methods#
- GetPackage - Query entire package
- GetFunction - Query function signatures
Need help? Contact support@dwellir.com or check the gRPC overview.