OS Trading Engine
Security
Wallet Security Best Practices

Wallet Security

Nexgent uses a non-custodial model where private keys remain on your infrastructure. This page covers how wallets are handled and best practices for securing them.

🚫

Critical: Private keys control real funds. A compromised key means total loss of funds with no recovery possible.

Architecture

┌─────────────────────────────────────────────────┐
│                 Your Infrastructure              │
│  ┌───────────────────────────────────────────┐  │
│  │         Environment Variables              │  │
│  │   WALLET_1=<base58_private_key>           │  │
│  │   WALLET_2=<base58_private_key>           │  │
│  └─────────────────────┬─────────────────────┘  │
│                        │                        │
│                        ▼                        │
│  ┌───────────────────────────────────────────┐  │
│  │           Wallet Loader (Startup)          │  │
│  │   Decode base58 → Keypair                 │  │
│  └─────────────────────┬─────────────────────┘  │
│                        │                        │
│                        ▼                        │
│  ┌───────────────────────────────────────────┐  │
│  │           In-Memory Wallet Store           │  │
│  │   Map<publicKey, Keypair>                 │  │
│  │   (Never persisted to disk/database)      │  │
│  └───────────────────────────────────────────┘  │
└─────────────────────────────────────────────────┘

Key Principles

1. Keys Never Leave Memory

Private keys are:

  • Loaded once at startup
  • Stored only in memory (Map)
  • Never written to database
  • Never logged
  • Never sent over network
// Wallet store - memory only
class WalletStore {
  private wallets: Map<string, Keypair> = new Map();
  
  getKeypair(address: string): Keypair | undefined {
    return this.wallets.get(address);
  }
}

2. Keys Never in Database

The database stores only public wallet addresses:

model AgentWallet {
  id            String @id
  walletAddress String @unique  // Public address only
  agentId       String
}

3. Keys Never Logged

// NEVER do this
console.log(keypair.secretKey);  // ❌
 
// Safe: log public key only
console.log(`Loaded wallet: ${publicKey.slice(0, 8)}...`);  // ✓

Configuration

Environment Variables

# Wallet private keys (base58 encoded)
# Export from Phantom: Settings > Security > Export Private Key
WALLET_1=your-base58-private-key-here
WALLET_2=another-private-key-here
⚠️

Wallets are only needed for live trading mode. Simulation mode doesn't require private keys.

Loading Process

// infrastructure/wallets/wallet-loader.ts
export class WalletLoader {
  loadWallets() {
    const wallets = new Map<string, Keypair>();
    
    for (let i = 1; i <= 10; i++) {
      const privateKey = process.env[`WALLET_${i}`];
      
      if (privateKey) {
        // Decode base58 to bytes
        const secretKey = bs58.decode(privateKey);
        
        // Create keypair
        const keypair = Keypair.fromSecretKey(secretKey);
        
        // Store by public key
        wallets.set(keypair.publicKey.toBase58(), keypair);
      }
    }
    
    return wallets;
  }
}

Best Practices

Use Dedicated Trading Wallets

Don't use your main wallet. Create separate wallets specifically for trading:

  1. Generate a new wallet in Phantom/Solflare
  2. Transfer only trading funds
  3. Keep main holdings separate

Minimize Wallet Balance

Only keep funds you're actively trading:

Risk LevelRecommendation
LowKeep 1-2 SOL for testing
MediumKeep 5-10 SOL for active trading
HighNever keep more than you can afford to lose

Keep Main Wallet Separate

Most users use browser wallets like Phantom or Solflare:

  1. Create a separate wallet specifically for Nexgent trading
  2. Keep your main holdings in your primary wallet
  3. Transfer only trading funds to the dedicated trading wallet
  4. Never use your primary wallet's private key with Nexgent

Rotate Wallets Periodically

  1. Create new trading wallet
  2. Transfer funds from old wallet
  3. Update WALLET_N environment variable
  4. Restart backend
  5. Reassign agents to new wallet

Simulation Mode

For testing without risk, use simulation mode:

// Agents in simulation mode use virtual wallets
const agent = await createAgent({
  name: 'Test Agent',
  tradingMode: 'simulation',  // No real wallet needed
  walletAddress: 'sim_virtual_wallet',
});

Simulation mode:

  • Doesn't require private keys
  • Uses virtual balances
  • Simulates trades without on-chain execution
  • Perfect for testing strategies

Transaction Signing

When executing live trades:

// 1. Get quote from Jupiter (unsigned transaction)
const quote = await jupiterProvider.getQuote({
  inputMint: SOL_MINT,
  outputMint: tokenAddress,
  amount: amountLamports,
  walletAddress: walletAddress,
});
 
// 2. Get keypair from memory
const keypair = walletStore.getKeypair(walletAddress);
 
// 3. Sign transaction locally
const transaction = VersionedTransaction.deserialize(
  Buffer.from(quote.transaction, 'base64')
);
transaction.sign([keypair]);
 
// 4. Submit signed transaction
const result = await jupiterProvider.submitTransaction(transaction);

The private key is used only for signing and never leaves the server.


Security Checklist

Setup

  • Use dedicated trading wallets (not main wallet)
  • Keep minimal balance in trading wallets
  • Store private keys in environment variables
  • Never commit .env files to git
  • Use .gitignore for all env files

Operations

  • Monitor wallet balances regularly
  • Review transactions for unexpected activity
  • Rotate trading wallets periodically
  • Use simulation mode for testing

Infrastructure

  • Restrict server access (SSH keys, no passwords)
  • Use secrets manager in production
  • Enable audit logging
  • Set up balance alerts

Emergency Procedures

Suspected Compromise

  1. Immediately transfer funds to a new wallet
  2. Revoke the old wallet from all agents
  3. Update environment variables
  4. Restart the backend
  5. Investigate the breach

Lost Private Key

If you lose access to a private key:

  • Funds in that wallet are permanently inaccessible
  • Always keep backups in a secure location
  • Consider using a password manager for key backup