Skip to content

Type System

LUMOS supports a rich type system that maps cleanly between Rust and TypeScript.

LUMOS TypeRust TypeTypeScript TypeBorsh Schema
u8u8numberborsh.u8()
u16u16numberborsh.u16()
u32u32numberborsh.u32()
u64u64numberborsh.u64()
u128u128bigintborsh.u128()
i8i8numberborsh.i8()
i16i16numberborsh.i16()
i32i32numberborsh.i32()
i64i64numberborsh.i64()
i128i128bigintborsh.i128()
boolboolbooleanborsh.bool()
StringStringstringborsh.string()
PublicKeyPubkeyPublicKeyborsh.publicKey()
SignatureSignatureUint8Arrayborsh.array(borsh.u8(), 64)
[T]Vec<T>T[]borsh.vec(...)
Option<T>Option<T>T | undefinedborsh.option(...)

#[solana]
struct Numbers {
tiny: u8, // 0 to 255
small: u16, // 0 to 65,535
medium: u32, // 0 to 4,294,967,295
large: u64, // 0 to 18,446,744,073,709,551,615
huge: u128, // 0 to 340,282,366,920,938,463,463,374,607,431,768,211,455
}

Rust output:

pub struct Numbers {
pub tiny: u8,
pub small: u16,
pub medium: u32,
pub large: u64,
pub huge: u128,
}

TypeScript output:

export interface Numbers {
tiny: number;
small: number;
medium: number;
large: number; // May lose precision > Number.MAX_SAFE_INTEGER
huge: bigint;
}

#[solana]
struct SignedNumbers {
tiny: i8, // -128 to 127
small: i16, // -32,768 to 32,767
medium: i32, // -2,147,483,648 to 2,147,483,647
large: i64, // Large signed range
huge: i128, // Massive signed range
}

TypeScript mapping:

  • i8, i16, i32, i64number
  • i128bigint

#[solana]
struct Flags {
is_active: bool,
has_permission: bool,
}

Borsh encoding:

  • true0x01
  • false0x00

#[solana]
struct Metadata {
name: String,
description: String,
symbol: String,
}

Borsh encoding:

  • Length prefix (4 bytes, little-endian)
  • UTF-8 bytes

Example:

  • "hello"0x05000000 + 0x68656c6c6f

Represents a Solana public key (32 bytes).

#[solana]
struct Account {
owner: PublicKey,
authority: PublicKey,
}

Rust mapping:

use anchor_lang::prelude::*;
pub struct Account {
pub owner: Pubkey,
pub authority: Pubkey,
}

TypeScript mapping:

import { PublicKey } from '@solana/web3.js';
export interface Account {
owner: PublicKey;
authority: PublicKey;
}
export const AccountBorshSchema = borsh.struct([
borsh.publicKey('owner'),
borsh.publicKey('authority'),
]);

Borsh encoding:

  • 32 bytes (fixed size)

Represents a Solana signature (64 bytes).

#[solana]
struct Transaction {
signature: Signature,
}

Rust mapping:

pub struct Transaction {
pub signature: Signature, // From solana_program
}

TypeScript mapping:

export interface Transaction {
signature: Uint8Array; // 64 bytes
}
export const TransactionBorshSchema = borsh.struct([
borsh.array(borsh.u8(), 64, 'signature'),
]);

Dynamic-length arrays.

#[solana]
struct Inventory {
items: [PublicKey],
scores: [u64],
}

Rust output:

pub struct Inventory {
pub items: Vec<Pubkey>,
pub scores: Vec<u64>,
}

TypeScript output:

export interface Inventory {
items: PublicKey[];
scores: number[];
}
export const InventoryBorshSchema = borsh.struct([
borsh.vec(borsh.publicKey(), 'items'),
borsh.vec(borsh.u64(), 'scores'),
]);

Borsh encoding:

  • Length prefix (4 bytes, little-endian)
  • Elements sequentially

Example:

  • [1, 2, 3]0x03000000 + 0x01 + 0x02 + 0x03

Nullable fields.

#[solana]
struct Profile {
username: String,
email: Option<String>,
avatar: Option<PublicKey>,
}

Rust output:

pub struct Profile {
pub username: String,
pub email: Option<String>,
pub avatar: Option<Pubkey>,
}

TypeScript output:

export interface Profile {
username: string;
email: string | undefined;
avatar: PublicKey | undefined;
}
export const ProfileBorshSchema = borsh.struct([
borsh.string('username'),
borsh.option(borsh.string(), 'email'),
borsh.option(borsh.publicKey(), 'avatar'),
]);

Borsh encoding:

  • None0x00
  • Some(value)0x01 + value bytes

Example:

  • None0x00
  • Some(42)0x01 + 0x2a

Rust-style enums with three variant types.

#[solana]
enum Status {
Active,
Paused,
Terminated,
}

Rust output:

pub enum Status {
Active,
Paused,
Terminated,
}

TypeScript output:

export type Status =
| { kind: 'Active' }
| { kind: 'Paused' }
| { kind: 'Terminated' };
export const StatusBorshSchema = borsh.rustEnum([
borsh.unit('Active'),
borsh.unit('Paused'),
borsh.unit('Terminated'),
]);

Borsh encoding:

  • Discriminant (1 byte) + variant data
  • Active0x00
  • Paused0x01
  • Terminated0x02

#[solana]
enum Event {
UserJoined(PublicKey),
ScoreUpdated(PublicKey, u64),
GameEnded(PublicKey, u64, i64),
}

Rust output:

pub enum Event {
UserJoined(Pubkey),
ScoreUpdated(Pubkey, u64),
GameEnded(Pubkey, u64, i64),
}

TypeScript output:

export type Event =
| { kind: 'UserJoined'; fields: [PublicKey] }
| { kind: 'ScoreUpdated'; fields: [PublicKey, number] }
| { kind: 'GameEnded'; fields: [PublicKey, number, number] };
export const EventBorshSchema = borsh.rustEnum([
borsh.tuple([borsh.publicKey()], 'UserJoined'),
borsh.tuple([borsh.publicKey(), borsh.u64()], 'ScoreUpdated'),
borsh.tuple([borsh.publicKey(), borsh.u64(), borsh.i64()], 'GameEnded'),
]);

#[solana]
enum Instruction {
Initialize {
authority: PublicKey,
max_users: u32,
},
UpdateConfig {
new_authority: PublicKey,
},
Terminate,
}

Rust output:

pub enum Instruction {
Initialize {
authority: Pubkey,
max_users: u32,
},
UpdateConfig {
new_authority: Pubkey,
},
Terminate,
}

TypeScript output:

export type Instruction =
| { kind: 'Initialize'; authority: PublicKey; max_users: number }
| { kind: 'UpdateConfig'; new_authority: PublicKey }
| { kind: 'Terminate' };
export const InstructionBorshSchema = borsh.rustEnum([
borsh.struct([
borsh.publicKey('authority'),
borsh.u32('max_users'),
], 'Initialize'),
borsh.struct([
borsh.publicKey('new_authority'),
], 'UpdateConfig'),
borsh.unit('Terminate'),
]);

The following types are NOT supported:

❌ Floating point (f32, f64) ❌ Fixed-size arrays ([u8; 32]) - use Vec or PublicKey ❌ Tuples ((u64, String)) - use structs instead ❌ References (&str, &[u8]) ❌ Raw pointers (*const, *mut) ❌ Function pointers

Fixed-size arrays → Vec:

// ❌ Not supported
struct Data {
buffer: [u8; 1024],
}
// ✅ Use Vec instead
struct Data {
buffer: [u8], // Vec<u8>
}

Tuples → Struct:

// ❌ Not supported
struct Pair {
data: (u64, String),
}
// ✅ Use struct instead
struct Pair {
first: u64,
second: String,
}