← Back to guides
April 18, 2026·10 min read

How Subglow's Copy Trader Works: Non-Custodial Mirror Trading on Solana (2026)

How Subglow's Copy Trader Works: Non-Custodial Mirror Trading on Solana (2026)
Copy tradingArchitectureNon-custodialSolanagRPCSubglow

We built the Subglow copy trader on top of our own gRPC product. This post is the engineering story — how the system is wired, which trade-offs we picked, and what you'd need to build one yourself.

If you want the short version: we filter Pump.fun, Raydium, and Jupiter events through Subglow gRPC, match them against a user-configured watchlist of KOL wallets, size the trade against the user's Solana balance, sign with a key that never leaves the user's device, and submit through Jito. End-to-end we target 200–400ms from validator slot to signed bundle.

The Problem We Were Solving

Every other Solana copy trader makes you hand over your keys. You deposit SOL into a custodial wallet, the platform signs on your behalf, and if they get hacked — or just feel like pausing withdrawals — your money is frozen. That's the industry default because it's dramatically easier to build: you run one signer on a server, latency is short, the code is simple.

We wanted copy trading that feels like a normal DEX interaction: your wallet, your keys, your SOL. But that choice ripples through every layer of the stack, so let me walk through how the system handles it.

Architecture at a Glance

Four components, all on the happy path in under half a second:

  1. Subglow gRPC — filtered Yellowstone stream. Only Pump.fun, Raydium, and Jupiter transactions hit the socket.
  2. Rust backend matcher — parses instructions, classifies buy/sell, checks the KOL watchlist, and emits an intent event.
  3. Browser-side signer — the user's private key sits in IndexedDB, encrypted at rest. On intent arrival, the browser signs a transaction without ever posting the key back to our servers.
  4. Jito relay submission — signed transaction becomes a bundle and goes out through our Jito endpoint in AMS.

Step 1: Catching the KOL Trade

The Rust service opens a single gRPC subscription against grpc.subglow.io with a SubscribeRequestFilterTransactions that matches the three programs we care about. Vote transactions and failed transactions are excluded server-side, which knocks ~80% of the raw Solana traffic off the wire before it even touches the network.

let filter = SubscribeRequestFilterTransactions {
  vote: Some(false),
  failed: Some(false),
  account_include: vec![
    PUMP_FUN_PROGRAM.to_string(),
    RAYDIUM_AMM_V4.to_string(),
    JUPITER_AGG_V6.to_string(),
  ],
  ..Default::default()
};

Because Subglow pre-parses Pump.fun, Raydium, and Jupiter instructions into JSON, the matcher doesn't have to Borsh-decode anything — we skip an entire deserialization layer. On a normal Sniper plan client this saves the bot ~5–15ms per event.

Step 2: Intent Generation

Each parsed transaction gets classified. For a Pump.fun bonding-curve swap we pull the mint, the signer, the SOL delta, and the token delta. If the signer is in the user's KOL watchlist, we compute an intent:

  • Side — buy or sell
  • Mint — the token the KOL touched
  • Notional — user's configured copy size (fixed SOL amount, or a % of the KOL's SOL delta)
  • Slippage — user's configured slippage band
  • Priority fee — user's configured microlamports per compute unit

The intent is published to a WebSocket channel dedicated to the user. Latency budget at this point: roughly 50–120ms, most of which is the gRPC push itself.

Step 3: The Non-Custodial Signer

This is the bit most teams skip and cost themselves a 50ms round trip on. The user's private key sits in the browser, encrypted with a password-derived key in IndexedDB. When they open the copy trader tab, the key is decrypted into memory and stays there until the tab closes.

When an intent lands over the WebSocket, the browser:

  1. Fetches a quote from Jupiter (or derives the swap route directly against the Pump.fun curve, for Pump.fun mints).
  2. Builds a versioned transaction with the correct priority fee and compute unit budget.
  3. Signs with the in-memory key — never posts the key or the raw transaction back to our backend.
  4. Submits the signed base64 blob to /api/submit, which forwards it to Jito.

This costs us one extra round trip (backend → browser → backend) versus a custodial design, and we spent weeks shaving that round trip down. The browser signer runs in a dedicated WebWorker so main-thread rendering doesn't steal cycles from it.

Step 4: Jito Submission

We submit as a bundle (not a regular sendTransaction) because bundles get landed by the Jito block-engine directly, without taking a trip through the mempool. For a sniper competing with other copy bots on a newly-minted Pump.fun token, that's the difference between being first in the block and being in block N+1.

Subglow's Jito endpoint is colocated in Amsterdam, which is where most of the Jito block-engine action is. For our EU users this saves another 20–40ms versus submitting from a US client.

End-to-End Latency Budget

Stagep50p99
Slot landed → Geyser hook3ms8ms
Geyser → gRPC client (AMS-colocated)30ms80ms
Rust matcher + intent publish10ms30ms
Intent → browser WebSocket40ms120ms
Quote fetch + build + sign (browser)80ms180ms
Browser → backend → Jito50ms140ms
Total slot→bundle~215ms~560ms

We target 200–400ms p50 in practice. Users on poor home connections can see p99 drift well over a second — that's the trade-off of browser-side signing we consciously accept.

What We'd Do Differently If We Had To Go Custodial

Honestly? Very little. The signing-in-browser round trip is the only penalty, and it's almost entirely network. The architectural benefits — you keep your keys, you keep your SOL, our database never holds anything that could get drained — outweigh the ~100ms hit.

If you want to try the system, check out Subglow Copy Trading. If you want to build your own, the gRPC layer it sits on top of is documented at Subglow gRPC — same filters, same pre-parsed output, from $99/mo flat.

Related Reading

Ready to try it?

Get your API key and start receiving filtered data in under 5 minutes. Free tier available.

Get started