Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.usewraith.xyz/llms.txt

Use this file to discover all available pages before exploring further.

Solana programs (smart contracts) for stealth address operations, written in Rust with the Anchor framework.

Program Set

ProgramPurpose
wraith-announcerEmits stealth address announcement events
wraith-senderAtomic SOL/SPL transfer + announcement
wraith-names.wraith name to meta-address mapping

wraith-announcer

Emits announcement events via Anchor’s emit!() macro. Stateless — no on-chain storage.

Instruction

pub fn announce(
    ctx: Context<Announce>,
    scheme_id: u32,
    stealth_address: Pubkey,
    ephemeral_pub_key: [u8; 32],
    metadata: Vec<u8>,
) -> Result<()>

Accounts

#[derive(Accounts)]
pub struct Announce<'info> {
    #[account(mut)]
    pub caller: Signer<'info>,
}

Event

#[event]
pub struct AnnouncementEvent {
    pub scheme_id: u32,
    pub stealth_address: Pubkey,
    pub caller: Pubkey,
    pub ephemeral_pub_key: [u8; 32],
    pub metadata: Vec<u8>,
}

Usage

import { SCHEME_ID } from "@wraith-protocol/sdk/chains/solana";
import { Program } from "@coral-xyz/anchor";

const program = new Program(idl, programId, provider);

await program.methods
  .announce(
    SCHEME_ID,
    stealthAddressPubkey,
    Array.from(ephemeralPubKey),
    Array.from(metadata),
  )
  .accounts({ caller: wallet.publicKey })
  .rpc();

wraith-sender

Atomic SOL or SPL token transfer + announcement in one instruction. Sends funds to the stealth address and emits an announcement event.

Instructions

send_sol

Transfer SOL to a stealth address and emit an announcement.
pub fn send_sol(
    ctx: Context<SendSol>,
    amount: u64,
    scheme_id: u32,
    stealth_address: Pubkey,
    ephemeral_pub_key: [u8; 32],
    metadata: Vec<u8>,
) -> Result<()>
Accounts:
#[derive(Accounts)]
pub struct SendSol<'info> {
    #[account(mut)]
    pub sender: Signer<'info>,
    /// CHECK: stealth address, receives SOL
    #[account(mut)]
    pub stealth_account: UncheckedAccount<'info>,
    pub system_program: Program<'info, System>,
}

send_spl

Transfer SPL tokens to a stealth address’s associated token account and emit an announcement.
pub fn send_spl(
    ctx: Context<SendSpl>,
    amount: u64,
    scheme_id: u32,
    stealth_address: Pubkey,
    ephemeral_pub_key: [u8; 32],
    metadata: Vec<u8>,
) -> Result<()>
Accounts:
#[derive(Accounts)]
pub struct SendSpl<'info> {
    #[account(mut)]
    pub sender: Signer<'info>,
    #[account(mut)]
    pub sender_token_account: Account<'info, TokenAccount>,
    #[account(mut)]
    pub stealth_token_account: Account<'info, TokenAccount>,
    pub token_program: Program<'info, Token>,
}

Usage

import { generateStealthAddress, SCHEME_ID } from "@wraith-protocol/sdk/chains/solana";
import { PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";

const stealth = generateStealthAddress(spendingPubKey, viewingPubKey);
const metadata = [stealth.viewTag]; // first byte is view tag

// Send SOL + announce atomically
await program.methods
  .sendSol(
    new BN(0.1 * LAMPORTS_PER_SOL),
    SCHEME_ID,
    new PublicKey(stealth.stealthAddress),
    Array.from(stealth.ephemeralPubKey),
    metadata,
  )
  .accounts({
    sender: wallet.publicKey,
    stealthAccount: new PublicKey(stealth.stealthAddress),
    systemProgram: SystemProgram.programId,
  })
  .rpc();

wraith-names

PDA-based name to meta-address mapping. Names are stored in Program Derived Addresses (PDAs) seeded by ["name", nameBytes].

Instructions

register

Register a new .wraith name.
pub fn register(
    ctx: Context<Register>,
    name: String,
    meta_address: [u8; 64],
) -> Result<()>

update

Update the meta-address for a name you own.
pub fn update(
    ctx: Context<Update>,
    new_meta_address: [u8; 64],
) -> Result<()>

release

Release a name. Closes the PDA account and returns rent to the owner.
pub fn release(ctx: Context<Release>) -> Result<()>

resolve

Look up a name’s meta-address. Read-only.
pub fn resolve(ctx: Context<Resolve>) -> Result<[u8; 64]>

Account Structure

#[account]
pub struct NameRecord {
    pub name: String,               // max 32 bytes
    pub meta_address: [u8; 64],     // spending pub + viewing pub
    pub owner: Pubkey,
    pub created_at: i64,
}

PDA Derivation

Name records are stored at PDAs derived from the name:
seeds = [b"name", name.as_bytes()]
This means names are globally unique and can be resolved without knowing the owner.

Validation

  • Name: 3-32 characters, lowercase alphanumeric and hyphens only
  • Meta-address: must be exactly 64 bytes (two 32-byte ed25519 public keys)
  • Only the owner can update or release a name

Error Codes

#[error_code]
pub enum WraithError {
    #[msg("Name must be 3-32 characters")]
    InvalidNameLength,
    #[msg("Name must be lowercase alphanumeric or hyphens")]
    InvalidNameCharacter,
    #[msg("Only the owner can modify this name")]
    NotOwner,
}

Usage

import { encodeStealthMetaAddress } from "@wraith-protocol/sdk/chains/solana";
import { PublicKey } from "@solana/web3.js";

const metaAddress = encodeStealthMetaAddress(keys.spendingPubKey, keys.viewingPubKey);

// Derive PDA for the name
const [nameRecordPda] = PublicKey.findProgramAddressSync(
  [Buffer.from("name"), Buffer.from("alice")],
  programId,
);

// Register
await program.methods
  .register("alice", Array.from(metaAddressBytes))
  .accounts({
    nameRecord: nameRecordPda,
    owner: wallet.publicKey,
    systemProgram: SystemProgram.programId,
  })
  .rpc();

// Resolve
const record = await program.account.nameRecord.fetch(nameRecordPda);
console.log(record.metaAddress); // [u8; 64]

Project Structure

contracts/
  solana/
    Anchor.toml
    Cargo.toml
    programs/
      wraith-announcer/
        Cargo.toml
        src/lib.rs
      wraith-sender/
        Cargo.toml
        src/lib.rs
      wraith-names/
        Cargo.toml
        src/lib.rs
    tests/
      wraith-announcer.ts
      wraith-sender.ts
      wraith-names.ts

Deployment

Build

anchor build

Deploy to Devnet

anchor deploy --provider.cluster devnet

Test

anchor test
Tests cover:
  • wraith-announcer — event emission, multiple callers, metadata preservation
  • wraith-sendersend_sol transfers + emits, send_spl token transfer + emits, insufficient funds rejection
  • wraith-names — register/resolve, name validation (too short, invalid chars), update by owner, update by non-owner (rejected), release and re-register

Differences from EVM and Stellar Contracts

AspectEVM (Solidity)Stellar (Soroban)Solana (Anchor)
LanguageSolidityRustRust (Anchor)
Contract modelBytecode deploymentWASM deploymentBPF program deployment
Name storageContract storageContract storagePDAs (Program Derived Addresses)
Name sig verificationOn-chain ECDSA recoveryCaller authSigner constraint
Event indexingSubgraph / The GraphSoroban RPC getEventsProgram transaction logs
Account modelAddress always existsMust createAccount firstAddress always valid, no deployment
Token transfersmsg.value / safeTransferFromSoroban token contractSystemProgram / SPL Token
Gas sponsorshipEIP-7702 (WraithWithdrawer)Not applicableFee payer pattern
Min balanceNone1 XLM~0.00089 SOL (rent exemption)