# GetDatatype - Query Move Struct Definitions

## 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.

## Overview

In Sui's Move programming model, every on-chain object is an instance of a Move struct. The `GetDatatype` method lets you inspect these struct definitions programmatically, revealing their fields, type parameters, and Move abilities (copy, drop, store, key). This is critical for developers who need to understand unfamiliar smart contracts, build generic transaction constructors, or parse event and object data without hardcoded type knowledge.

Move's ability system is unique to the language: it controls how values can be copied, dropped, stored in global storage, or used as object identifiers. Understanding a struct's abilities tells you exactly what operations are valid for that type. For example, a struct with the `key` ability can exist as a standalone object on Sui, while `store` allows it to be nested inside other objects.

### Key Capabilities

- **Struct Inspection**: Retrieve field names, types, and positions for any deployed struct
- **Ability Analysis**: Check whether a type can be copied, dropped, stored, or used as an object
- **Generic Type Support**: Inspect type parameters and their constraints (phantom types, ability bounds)
- **Enum Support**: Query enum variants and their associated data fields
- **Event Parsing**: Use struct definitions to deserialize event data at runtime
- **Schema Generation**: Automatically generate TypeScript interfaces or Python dataclasses from Move types

## Method Signature

**Service**: `sui.rpc.v2.MovePackageService`
**Method**: `GetDatatype`
**Type**: Unary RPC

## Request Parameters

- `package_id` (`string, required`): Package object ID
- `module_name` (`string, required`): Module name within package
- `datatype_name` (`string, required`): Struct/type name within module

## Request Example

```bash
# Get the Coin struct from the Sui Framework
    grpcurl \
      -H "x-api-key: YOUR_API_KEY" \
      -d '{
        "package_id": "0x0000000000000000000000000000000000000000000000000000000000000002",
        "module_name": "coin",
        "datatype_name": "Coin"
      }' \
      api-sui-mainnet-full.n.dwellir.com:443 \
      sui.rpc.v2.MovePackageService/GetDatatype

    # Get the Balance struct
    grpcurl \
      -H "x-api-key: YOUR_API_KEY" \
      -d '{
        "package_id": "0x0000000000000000000000000000000000000000000000000000000000000002",
        "module_name": "balance",
        "datatype_name": "Balance"
      }' \
      api-sui-mainnet-full.n.dwellir.com:443 \
      sui.rpc.v2.MovePackageService/GetDatatype
```

## Response Fields

- `name` (`string, optional`): No description provided.
- `abilities` (`MoveAbility[], optional`): No description provided.
- `type_parameters` (`MoveTypeParameter[], optional`): No description provided.
- `fields` (`MoveField[], optional`): No description provided.
- `name` (`string, optional`): No description provided.
- `type` (`MoveType, optional`): No description provided.

## Successful Response

```protobuf
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;
}
```

## Use Cases

### Generate TypeScript Interface

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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](https://www.dwellir.com/docs/sui/grpc/package/get-package) - Query entire package
- [GetFunction](https://www.dwellir.com/docs/sui/grpc/package/get-function) - Query function signatures

***

*Need help? Contact <support@dwellir.com> or check the [gRPC overview](https://www.dwellir.com/docs/sui/grpc-overview).*
