Generated Code Examples
This page shows complete examples of what LUMOS generates from .lumos schemas. Each example includes the input schema and both Rust and TypeScript outputs.
Simple Account Structure
Section titled “Simple Account Structure”Input Schema
Section titled “Input Schema”#[solana]#[account]struct PlayerAccount { wallet: PublicKey, level: u16, experience: u64,}Generated Rust
Section titled “Generated Rust”// Auto-generated by LUMOS// DO NOT EDIT - Changes will be overwritten
use anchor_lang::prelude::*;use solana_program::pubkey::Pubkey;
#[account]pub struct PlayerAccount { pub wallet: Pubkey, pub level: u16, pub experience: u64,}Key points:
#[account]macro applied (Anchor integration)PublicKey→Pubkeyconversion- All fields are
pub - No manual Borsh derives needed (handled by Anchor)
Generated TypeScript
Section titled “Generated TypeScript”// Auto-generated by LUMOS// DO NOT EDIT - Changes will be overwritten
import * as borsh from '@coral-xyz/borsh';import { PublicKey } from '@solana/web3.js';
export interface PlayerAccount { wallet: PublicKey; level: number; /** * WARNING: TypeScript 'number' has precision limit of 2^53-1 (9,007,199,254,740,991). * For Solana lamports or large values, ensure they stay within safe range. * Values exceeding this limit will lose precision during serialization. */ experience: number;}
export const PlayerAccountSchema = borsh.struct([ borsh.publicKey('wallet'), borsh.u16('level'), borsh.u64('experience'),]);Key points:
- Interface for TypeScript type safety
- Borsh schema for serialization/deserialization
u16,u64→number(JavaScript safe integers)- Auto-generated precision warnings for u64/i64 fields
Event Data (No #[account])
Section titled “Event Data (No #[account])”Input Schema
Section titled “Input Schema”#[solana]struct MatchResult { winner: PublicKey, loser: Option<PublicKey>, score: u64, timestamp: i64,}Generated Rust
Section titled “Generated Rust”use anchor_lang::prelude::*;
pub struct MatchResult { pub winner: Pubkey, pub loser: Option<Pubkey>, pub score: u64, pub timestamp: i64,}
impl borsh::ser::BorshSerialize for MatchResult { fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> { borsh::BorshSerialize::serialize(&self.winner, writer)?; borsh::BorshSerialize::serialize(&self.loser, writer)?; borsh::BorshSerialize::serialize(&self.score, writer)?; borsh::BorshSerialize::serialize(&self.timestamp, writer)?; Ok(()) }}
impl borsh::de::BorshDeserialize for MatchResult { fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> { Ok(Self { winner: borsh::BorshDeserialize::deserialize_reader(reader)?, loser: borsh::BorshDeserialize::deserialize_reader(reader)?, score: borsh::BorshDeserialize::deserialize_reader(reader)?, timestamp: borsh::BorshDeserialize::deserialize_reader(reader)?, }) }}Key differences:
- NO
#[account]macro - Manual Borsh trait implementations
- Used for events, instruction arguments, return values
Generated TypeScript
Section titled “Generated TypeScript”import { PublicKey } from '@solana/web3.js';import * as borsh from '@coral-xyz/borsh';
export interface MatchResult { winner: PublicKey; loser: PublicKey | undefined; score: number; timestamp: number;}
export const MatchResultSchema = borsh.struct([ borsh.publicKey('winner'), borsh.option(borsh.publicKey(), 'loser'), borsh.u64('score'), borsh.i64('timestamp'),]);Key points:
Option<PublicKey>→PublicKey | undefinedborsh.option()handles nullable fields
Complex Types (Vectors)
Section titled “Complex Types (Vectors)”Input Schema
Section titled “Input Schema”#[solana]#[account]struct Inventory { owner: PublicKey, items: [PublicKey], quantities: [u32],}Generated Rust
Section titled “Generated Rust”use anchor_lang::prelude::*;
#[account]pub struct Inventory { pub owner: Pubkey, pub items: Vec<Pubkey>, pub quantities: Vec<u32>,}Key points:
[T]→Vec<T>in Rust- Dynamic-length arrays
Generated TypeScript
Section titled “Generated TypeScript”import { PublicKey } from '@solana/web3.js';import * as borsh from '@coral-xyz/borsh';
export interface Inventory { owner: PublicKey; items: PublicKey[]; quantities: number[];}
export const InventorySchema = borsh.struct([ borsh.publicKey('owner'), borsh.vec(borsh.publicKey(), 'items'), borsh.vec(borsh.u32(), 'quantities'),]);Key points:
[T]→T[]in TypeScriptborsh.vec()for dynamic arrays
Enum Types
Section titled “Enum Types”Input Schema
Section titled “Input Schema”#[solana]enum GameState { Active, Paused { reason: String }, Finished { winner: PublicKey, score: u64 },}Generated Rust
Section titled “Generated Rust”// Auto-generated by LUMOS// DO NOT EDIT - Changes will be overwritten
use borsh::{BorshSerialize, BorshDeserialize};use solana_program::pubkey::Pubkey;
#[derive(BorshSerialize, BorshDeserialize, Debug, Clone)]pub enum GameState { Active, Paused { reason: String, }, Finished { winner: Pubkey, score: u64, },}Key points:
- Uses
#[derive]macros for automatic serialization - Includes
DebugandClonetraits - Sequential discriminants (0, 1, 2…) handled by Borsh
- Cleaner code compared to manual implementations
Generated TypeScript
Section titled “Generated TypeScript”// Auto-generated by LUMOS// DO NOT EDIT - Changes will be overwritten
import * as borsh from '@coral-xyz/borsh';import { PublicKey } from '@solana/web3.js';
/** * WARNING: Some variants contain TypeScript 'number' types with precision limit of 2^53-1. * Large values (e.g., Solana lamports) may lose precision during serialization. */export type GameState = | { kind: 'Active' } | { kind: 'Paused'; reason: string } | { kind: 'Finished'; winner: PublicKey; score: number };
export const GameStateSchema = borsh.rustEnum([ borsh.unit('Active'), borsh.struct([ borsh.string('reason'), ], 'Paused'), borsh.struct([ borsh.publicKey('winner'), borsh.u64('score'), ], 'Finished'),]);Key points:
- Discriminated union type with
kindfield - TypeScript type narrowing support
borsh.rustEnum()for Rust-style enums
Non-Solana Types (Pure Borsh)
Section titled “Non-Solana Types (Pure Borsh)”Input Schema
Section titled “Input Schema”struct GenericData { id: u64, value: String, metadata: Option<String>,}Note: No #[solana] attribute
Generated Rust
Section titled “Generated Rust”use borsh::{BorshSerialize, BorshDeserialize};
#[derive(BorshSerialize, BorshDeserialize)]pub struct GenericData { pub id: u64, pub value: String, pub metadata: Option<String>,}Key differences:
- Uses
borshcrate directly (notanchor_lang) #[derive]macros for serialization- No Solana-specific types available
Generated TypeScript
Section titled “Generated TypeScript”import * as borsh from '@coral-xyz/borsh';
export interface GenericData { id: number; value: string; metadata: string | undefined;}
export const GenericDataSchema = borsh.struct([ borsh.u64('id'), borsh.string('value'), borsh.option(borsh.string(), 'metadata'),]);Same as Solana types - Borsh schema is identical whether #[solana] is used or not.
Import Strategy
Section titled “Import Strategy”LUMOS intelligently chooses imports based on attributes in your entire module:
Scenario 1: Any #[account] Present
Section titled “Scenario 1: Any #[account] Present”// ANY struct has #[account] → Use Anchor imports for ENTIRE moduleuse anchor_lang::prelude::*;
#[account]pub struct PlayerAccount { /* ... */ }
pub struct GameEvent { /* ... */ } // Also uses Anchor importsScenario 2: Only #[solana] (No #[account])
Section titled “Scenario 2: Only #[solana] (No #[account])”// Has #[solana] but no #[account] → Still use Anchoruse anchor_lang::prelude::*;
pub struct MatchResult { /* ... */ }// Manual Borsh impls addedScenario 3: No Solana Attributes
Section titled “Scenario 3: No Solana Attributes”// Pure Borsh typesuse borsh::{BorshSerialize, BorshDeserialize};
#[derive(BorshSerialize, BorshDeserialize)]pub struct GenericData { /* ... */ }Context-Aware Generation
Section titled “Context-Aware Generation”LUMOS detects the context of your entire schema file to generate optimal code.
Example: Mixed Module
Section titled “Example: Mixed Module”Input:
#[solana]#[account]struct PlayerAccount { wallet: PublicKey, level: u16,}
#[solana]struct GameEvent { player: PublicKey, action: String,}
#[solana]enum GameState { Active, Paused,}Generated Rust:
use anchor_lang::prelude::*;
#[account]pub struct PlayerAccount { pub wallet: Pubkey, pub level: u16,}
pub struct GameEvent { pub player: Pubkey, pub action: String,}
impl borsh::ser::BorshSerialize for GameEvent { /* ... */ }impl borsh::de::BorshDeserialize for GameEvent { /* ... */ }
pub enum GameState { Active, Paused,}
impl borsh::ser::BorshSerialize for GameState { /* ... */ }impl borsh::de::BorshDeserialize for GameState { /* ... */ }Key insight: Since ONE type has #[account], LUMOS uses anchor_lang::prelude::* for the entire module.
Using Generated Code
Section titled “Using Generated Code”In Anchor Programs (Rust)
Section titled “In Anchor Programs (Rust)”use anchor_lang::prelude::*;
// Import your generated typesuse crate::generated::*;
#[program]pub mod my_game { use super::*;
pub fn create_player(ctx: Context<CreatePlayer>) -> Result<()> { let player = &mut ctx.accounts.player; player.wallet = *ctx.accounts.user.key; player.level = 1; player.experience = 0; Ok(()) }}
#[derive(Accounts)]pub struct CreatePlayer<'info> { #[account( init, payer = user, space = 8 + std::mem::size_of::<PlayerAccount>() )] pub player: Account<'info, PlayerAccount>, // ← Generated type #[account(mut)] pub user: Signer<'info>, pub system_program: Program<'info, System>,}In TypeScript Clients
Section titled “In TypeScript Clients”import { PlayerAccount, PlayerAccountSchema } from './generated';import { PublicKey } from '@solana/web3.js';
// Serialize account dataconst accountData: PlayerAccount = { wallet: new PublicKey('...'), level: 5, experience: 1000,};
const serialized = borsh.serialize( PlayerAccountSchema, accountData);
// Deserialize from on-chain dataconst buffer = await connection.getAccountInfo(accountAddress);const deserialized = borsh.deserialize( PlayerAccountSchema, buffer.data) as PlayerAccount;
console.log(`Level: ${deserialized.level}`);console.log(`XP: ${deserialized.experience}`);Best Practices
Section titled “Best Practices”Formatting
Section titled “Formatting”Generated code is automatically formatted if you have the formatters installed:
- Rust:
rustfmt(viacargo fmt) - TypeScript:
prettier
See Also
Section titled “See Also”- CLI Commands - How to generate code
- Type System - All supported types
- Attributes -
#[solana],#[account] - Quick Start - End-to-end tutorial