Delegate Accounts
A delegate is a separate keypair authorized to trade on behalf of your main account. Orders placed by a delegate are attributed to the delegator’s account — they share the same balances, positions, and margin.
Why use delegates
- Security: your main wallet key stays in your wallet; the delegate key is the only secret you handle - if the delegate key is compromised, your main wallet remains untouched
- Scoped access: delegates can only trade, but cannot deposit or withdraw
- Revocable: delegate access can be removed anytime by your main account
- Visibility: all trades made by delegates appear in the webapp UI under your account
Setup
1. Generate a keypair
Generate an ed25519 keypair locally. This will be your delegate key.
Solana CLI (easiest):
solana-keygen new --outfile delegate-key.json --no-bip39-passphrase
solana-keygen pubkey delegate-key.json
The generated file contains the 64-byte secret key (first 32 bytes are the private key, last 32 are the public key). The printed public key (base58) is what you register in the webapp.
Rust:
#![allow(unused)]
fn main() {
use ed25519_dalek::SigningKey;
let keypair = SigningKey::generate(&mut rand::thread_rng());
let public_key = hex::encode(keypair.verifying_key().to_bytes());
let private_key = hex::encode(keypair.to_bytes());
println!("public key: {public_key}"); // register this in the webapp
println!("private key: {private_key}"); // use this to sign API transactions
}
Python:
from nacl.signing import SigningKey
keypair = SigningKey.generate()
public_key = keypair.verify_key.encode().hex()
private_key = keypair.encode().hex()
print(f"public key: {public_key}") # register this in the webapp
print(f"private key: {private_key}") # use this to sign API transactions
Store the private key securely. You will need the public key for the next step.
2. Register the delegate in the webapp
- Sign in at app.bullet.xyz
- Navigate to More → Delegate Accounts
- Enter a name and your delegate’s public key
- Confirm — this signs a delegation transaction with your main wallet
3. Trade with the delegate key
Sign transactions with the delegate’s private key. The exchange resolves the delegate to your main account automatically — no special fields or flags needed.
#![allow(unused)]
fn main() {
// sign with the delegate keypair — order executes on the main account
let order_bytes = create_place_order_bytes(
&delegate_keypair,
chain_id,
&chain_hash,
market_id,
orders,
);
}
See Transaction Signing for the full signing flow.
Note: For read endpoints (account info, balances, positions, open orders), always use your main account’s address — not the delegate’s. All state lives on the main account.
Capabilities
| Operation | Delegate | Main wallet |
|---|---|---|
| Place / cancel orders | Yes | Yes |
| Deposit | Yes | Yes |
| Withdraw | No | Yes |
Revoking a delegate
From the webapp, go to More → Delegate Accounts and remove the delegate. Revocation is immediate — the delegate key can no longer submit transactions for your account.
Limits
- Up to 10 delegates per account
- Delegate names are max 20 characters
- A delegate address cannot already have its own account
- A delegate cannot be registered to multiple accounts