Tired of maintaining duplicate type definitions in Rust and TypeScript? Here's how to migrate your existing Borsh code to LUMOS.
Why Migrate?
- ✅ Zero runtime overhead - Generated code is identical to manual Borsh
- ✅ Type synchronization - TypeScript and Rust always match
- ✅ Reduced maintenance - Single source of truth
- ✅ Fewer bugs - Eliminates serialization mismatches
Migration time: 5-30 minutes per struct/enum
Step 1: Audit Your Existing Code
Find All Borsh Types
Rust:
# Find structs with Borsh derives
rg '#\[derive.*Borsh' --type rust
# Find Account macros
rg '#\[account\]' --type rust
TypeScript:
# Find Borsh schemas
rg 'borsh\.(struct|rustEnum)' --type ts
Step 2: Before & After Example
Before (Manual Borsh)
Rust (state.rs):
use anchor_lang::prelude::*;
#[account]
pub struct PlayerAccount {
pub wallet: Pubkey,
pub level: u16,
pub experience: u64,
pub items: Vec<Pubkey>,
}
TypeScript (types.ts):
import { PublicKey } from '@solana/web3.js';
import * as borsh from '@coral-xyz/borsh';
export interface PlayerAccount {
wallet: PublicKey;
level: number;
experience: number;
items: PublicKey[];
}
export const PlayerAccountSchema = borsh.struct([
borsh.publicKey('wallet'),
borsh.u16('level'),
borsh.u64('experience'),
borsh.vec(borsh.publicKey(), 'items'),
]);
After (LUMOS Schema)
Create schema/player.lumos:
#[solana]
#[account]
struct PlayerAccount {
wallet: PublicKey,
level: u16,
experience: u64,
items: [PublicKey],
}
Generate code:
lumos generate schema/player.lumos
That's it! Both Rust and TypeScript are generated automatically.
Step 3: Update Imports
Rust:
// OLD:
use crate::state::PlayerAccount;
// NEW:
mod generated;
use generated::PlayerAccount;
TypeScript:
// OLD:
import { PlayerAccount, PlayerAccountSchema } from './types';
// NEW:
import { PlayerAccount, PlayerAccountBorshSchema } from './generated';
Step 4: Verify Binary Compatibility
Critical: Ensure LUMOS-generated code produces identical bytes.
#[test]
fn test_borsh_compatibility() {
let player = PlayerAccount {
wallet: Pubkey::new_unique(),
level: 10,
experience: 5000,
items: vec![Pubkey::new_unique()],
};
// Serialize with manual implementation
let manual_bytes = player.try_to_vec().unwrap();
// Serialize with LUMOS-generated
let generated_bytes = player.try_to_vec().unwrap();
assert_eq!(manual_bytes, generated_bytes, "Binary output must match!");
}
Type Mapping Reference
| Manual Rust | Manual TypeScript | LUMOS |
|---|---|---|
Pubkey |
PublicKey |
PublicKey |
u16 |
number |
u16 |
u64 |
number |
u64 |
Vec<T> |
T[] |
[T] |
Option<T> |
`T \ | undefined` |
String |
string |
String |
bool |
boolean |
bool |
Migration Checklist
- [ ] Install LUMOS CLI:
cargo install lumos-cli - [ ] Create
.lumosschema files - [ ] Generate Rust and TypeScript code
- [ ] Update imports in existing code
- [ ] Run binary compatibility tests
- [ ] Remove old manual type definitions
- [ ] Update CI/CD to regenerate on schema changes
Gradual Migration Strategy
You don't have to migrate everything at once:
- Phase 1: New types → Use LUMOS from the start
- Phase 2: High-churn types → Migrate types you change often
- Phase 3: Stable types → Migrate remaining types
CI/CD Integration
# .github/workflows/codegen.yml
name: Generate LUMOS Code
on: [push, pull_request]
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: cargo install lumos-cli
- run: lumos generate schema/*.lumos
- run: git diff --exit-code || (echo "Generated files outdated!" && exit 1)
Get Started
cargo install lumos-cli
lumos generate your-schema.lumos
- Documentation: https://docs.lumos-lang.org/guides/migration-guide/
- GitHub: https://github.com/getlumos/lumos
Questions about migration? Drop them below!
Top comments (0)