API Reference/StealthKeypair

StealthKeypair

A derived keypair for a specific stealth address. Used to sign transactions and claim funds from stealth payments.

Quick Overview

Module:onyx_sdk::core
Traits:Clone, Zeroize

Security Critical

StealthKeypair contains private key material. It implements Zeroize to securely clear memory when dropped. Never log, serialize, or persist these keypairs.

Definition

struct
rust
pub struct StealthKeypair {
    /// The derived secret key for this stealth address
    secret_key: SecretKey,
    /// The corresponding public key (stealth address)
    public_key: PublicKey,
}

Note: Fields are private. Access the public key via address() and use to_solana_keypair() for signing.

Constructor Methods

derive
pub fn derive(meta: &StealthMetaAddress, ephemeral_pubkey: &[u8; 32]) -> Result<Self>

Derives the spending keypair for a stealth address using your meta-address and the sender's ephemeral public key.

use onyx_sdk::prelude::*;

let meta = StealthMetaAddress::load_from_file("~/.onyx/keys.json")?;

// From a scanned payment
let payment = payments.first().unwrap();
let keypair = StealthKeypair::derive(&meta, &payment.ephemeral_pubkey)?;

// Verify the derivation matches
assert_eq!(keypair.address(), payment.stealth_address);
from_payment
pub fn from_payment(meta: &StealthMetaAddress, payment: &StealthPayment) -> Result<Self>

Convenience method to derive a keypair directly from a StealthPayment.

// Equivalent to StealthKeypair::derive(&meta, &payment.ephemeral_pubkey)
let keypair = StealthKeypair::from_payment(&meta, &payment)?;

Accessor Methods

address
pub fn address(&self) -> Pubkey

Returns the Solana public key (address) for this keypair.

let keypair = StealthKeypair::derive(&meta, &ephemeral_pubkey)?;

let address = keypair.address();
println!("Stealth address: {}", address);

// Use for transaction building
let ix = system_instruction::transfer(
    &address,          // from (stealth address)
    &destination,      // to
    amount,
);
pubkey_bytes
pub fn pubkey_bytes(&self) -> [u8; 32]

Returns the public key as raw bytes.

let bytes = keypair.pubkey_bytes();
println!("Public key bytes: {:?}", bytes);

Conversion Methods

to_solana_keypair
pub fn to_solana_keypair(&self) -> Result<solana_sdk::signature::Keypair>

Converts to a Solana SDK Keypair for signing transactions.

use solana_sdk::signature::{Keypair, Signer};

let stealth_kp = StealthKeypair::derive(&meta, &ephemeral_pubkey)?;
let solana_kp: Keypair = stealth_kp.to_solana_keypair()?;

// Now use with Solana SDK
println!("Pubkey: {}", solana_kp.pubkey());

// Sign a transaction
let tx = Transaction::new_signed_with_payer(
    &[instruction],
    Some(&solana_kp.pubkey()),
    &[&solana_kp],
    recent_blockhash,
);
to_bytes
pub fn to_bytes(&self) -> [u8; 64]

Returns the keypair as 64 bytes (32 secret + 32 public). Handle with extreme care.

Danger: This exposes raw private key bytes. Only use when absolutely necessary, and ensure the bytes are securely erased after use.

Signing Transactions

The primary use of StealthKeypair is signing transactions to move funds:

sign_transaction.rs
rust
use onyx_sdk::prelude::*;
use solana_sdk::{
    signature::Signer,
    system_instruction,
    transaction::Transaction,
};

fn claim_payment(
    meta: &StealthMetaAddress,
    payment: &StealthPayment,
    destination: &Pubkey,
    rpc: &RpcClient,
) -> Result<Signature> {
    // 1. Derive the keypair
    let keypair = StealthKeypair::from_payment(meta, payment)?;
    let signer = keypair.to_solana_keypair()?;

    // 2. Build the transfer instruction
    let transfer_ix = system_instruction::transfer(
        &payment.stealth_address,
        destination,
        payment.balance,
    );

    // 3. Create and sign transaction
    let recent_blockhash = rpc.get_latest_blockhash()?;

    let tx = Transaction::new_signed_with_payer(
        &[transfer_ix],
        Some(&payment.stealth_address),
        &[&signer],
        recent_blockhash,
    );

    // 4. Send transaction
    let signature = rpc.send_and_confirm_transaction(&tx)?;

    Ok(signature)
}

SPL Token Transfers

For SPL token payments, use the token program instructions:

claim_tokens.rs
rust
use onyx_sdk::prelude::*;
use spl_token::instruction as token_instruction;
use spl_associated_token_account::get_associated_token_address;

fn claim_token_payment(
    meta: &StealthMetaAddress,
    payment: &StealthPayment,
    destination_wallet: &Pubkey,
    rpc: &RpcClient,
) -> Result<Signature> {
    // Ensure this is a token payment
    let token_mint = payment.token_mint
        .ok_or(OnyxError::NotTokenPayment)?;
    let source_token_account = payment.token_account
        .ok_or(OnyxError::NotTokenPayment)?;

    // Derive keypair
    let keypair = StealthKeypair::from_payment(meta, payment)?;
    let signer = keypair.to_solana_keypair()?;

    // Get destination token account
    let dest_token_account = get_associated_token_address(
        destination_wallet,
        &token_mint,
    );

    // Get token balance
    let token_balance = rpc
        .get_token_account_balance(&source_token_account)?
        .amount
        .parse::<u64>()?;

    // Build transfer instruction
    let transfer_ix = token_instruction::transfer(
        &spl_token::id(),
        &source_token_account,
        &dest_token_account,
        &payment.stealth_address,
        &[],
        token_balance,
    )?;

    // Sign and send
    let tx = Transaction::new_signed_with_payer(
        &[transfer_ix],
        Some(&payment.stealth_address),
        &[&signer],
        rpc.get_latest_blockhash()?,
    );

    let signature = rpc.send_and_confirm_transaction(&tx)?;
    Ok(signature)
}

Memory Safety

StealthKeypair implements secure memory handling:

Zeroize on Drop

Private key bytes are securely zeroed when the keypair goes out of scope.

No Serialize

Cannot be serialized to prevent accidental persistence of private keys.

No Debug Output

Debug trait does not expose private key material.

{
    let keypair = StealthKeypair::derive(&meta, &ephemeral)?;

    // Use the keypair
    let tx = sign_transaction(&keypair)?;

} // keypair dropped here, private key memory is zeroed

// Attempting to use keypair here would be a compile error