Skip to content

Setting Up a Client Account

A client (also called the owner) is the person whose funds are held in on-chain vaults. Clients choose an advisor, deposit tokens, and approve or reject trade proposals.

  • A Solana wallet (Phantom, Solflare, Backpack, etc.) with some SOL for transaction fees
  • The pubkey of the advisor you want to work with

Open the Beluga app and connect your wallet using the wallet adapter button. The app uses @solana/wallet-adapter-react and supports all major Solana wallets.

When you first interact with Beluga, your wallet signs a fixed message to derive an x25519 encryption keypair:

beluga:x25519:v1

This signature is hashed with SHA-512. The first 32 bytes are clamped per RFC 7748 to produce your x25519 secret key, and the corresponding public key is derived from it. This keypair is deterministic — the same wallet always produces the same key.

The public key is stored on-chain so your advisor can encrypt proposals that only you can decrypt.

The advisory account is a Program Derived Address (PDA) seeded with ["advisory", <your_pubkey>]. Each wallet can have one advisory account at a time.

The on-chain instruction requires:

  • Signer: Your wallet (the owner)
  • Advisor: The pubkey of your chosen advisor
  • Encryption key: Your x25519 public key (32 bytes)
import { createAdvisoryAccount } from "./lib/program/client";
const txSig = await createAdvisoryAccount(
wallet,
connection,
advisorPubkey,
ownerEncryptionKey, // Uint8Array, 32 bytes
sendTransaction,
);

The advisory account starts in Pending status. It becomes Active once the advisor accepts.

Your advisor must call acceptAdvisoryAccount to activate the relationship. Until then, no proposals can be created. The advisor sets their fee rate (in basis points, max 100 bps = 1%) when accepting.

Once the advisory account is active, deposit tokens into your vault. Each token mint has its own vault PDA derived from ["vault", <advisory_account>, <token_mint>].

import { deposit } from "./lib/program/client";
const txSig = await deposit(
wallet,
connection,
advisoryAccountAddress,
tokenMint,
ownerTokenAccount, // your ATA for this token
amount, // raw token amount (lamports / smallest unit)
sendTransaction,
);

The program initializes the vault automatically if it doesn’t exist. For native SOL deposits, the client wraps SOL into wSOL, deposits, and the wSOL ATA is closed afterward to reclaim rent.

When your advisor creates a proposal, it appears on your dashboard as encrypted data. The app decrypts it using your x25519 secret key and displays the trade details:

  • Rationale: Why the advisor recommends this trade
  • Token pair: Which tokens are being swapped
  • Amount and direction: How much and which way (A to B, or B to A)
  • Pool: Which Raydium AMM pool to use
  • Slippage threshold: Minimum output amount

You can approve (setting an execution window of 60s to 24h) or reject the proposal.

Withdraw from any vault at any time:

import { withdraw } from "./lib/program/client";
const txSig = await withdraw(
wallet,
connection,
advisoryAccountAddress,
vaultAddress,
ownerTokenAccount,
tokenMint,
amount,
sendTransaction,
);

If you want to end the advisory relationship without closing the account:

  • Revoke: Sets the account to Revoked status. No new proposals can be created. Existing approved proposals can still be executed until their deadline.
  • Close: Closes the advisory account entirely and returns rent to the owner. All proposals must be in a terminal state first.