Skip to content

Adding an Advisory Account

An advisory account is the on-chain link between a client (owner) and their advisor. It’s a PDA that tracks the relationship status, fee rate, proposal counts, and encryption keys.

Created (Pending) → Accepted (Active) → Revoked or Closed

The client calls createAdvisoryAccount with:

  • Their x25519 public key (for encrypted proposals)
  • The advisor’s Solana wallet pubkey
const [pda, bump] = PublicKey.findProgramAddressSync(
[Buffer.from("advisory"), ownerPubkey.toBuffer()],
PROGRAM_ID,
);

Each owner can have one advisory account at a time. If one already exists, the client must close it before creating a new one.

Initial state:

FieldValue
statusPending
advisor_fee_bps0
proposal_count0
active_proposals0
owner_encryption_keyClient’s x25519 pubkey

The advisor signs acceptAdvisoryAccount to activate the relationship:

  • Verifies the advisor field matches their pubkey
  • Sets advisor_fee_bps (0-100 bps, max 1%)
  • Transitions status: Pending -> Active

If the advisor doesn’t recognize or want the client, they simply don’t accept. There is no explicit reject — the account stays in Pending until accepted or closed by the owner.

Once active, the full proposal lifecycle is available:

  1. Advisor creates proposal — encrypted payload stored on-chain, active_proposals incremented
  2. Client approves/rejects — if approved, sets execution parameters and deadline
  3. Advisor executes — swaps tokens via Raydium CPI, fees deducted
  4. Either party closes proposal — reclaims rent once in terminal state

Revoke (owner only):

  • Sets status to Revoked
  • No new proposals can be created
  • Existing approved proposals with time remaining can still be executed

Close (owner only):

  • Closes the account and returns rent to the owner
  • All proposals must already be closed (terminal state)
  • Works with both legacy (94-byte) and current (126-byte) account sizes

If a client needs to rotate their x25519 key (e.g., if they suspect compromise), they can call setEncryptionKey:

import { setEncryptionKey } from "./lib/program/client";
const txSig = await setEncryptionKey(
wallet,
connection,
newOwnerEncryptionKey, // Uint8Array, 32 bytes
sendTransaction,
);

After rotation, the advisor must use the new key for future proposals. Previously encrypted proposals remain decryptable with the old key (which the client still derives from the same wallet signature).

Each token has a separate vault PDA:

seeds = ["vault", advisory_account_pubkey, token_mint_pubkey]

Vaults are token accounts owned by the program’s authority PDA. They are created lazily — either explicitly via initVault or automatically during the first deposit.

Key properties:

  • Only the owner can deposit and withdraw
  • The advisor can only move funds between vaults via approved, executed proposals
  • Vaults persist independently of the advisory account — close them separately to reclaim rent