CatScope Botter Lifecycle: Graph Subscription & Account Tracking

Your bot taps directly into the validator’s live account graph, streaming decoded state and relationships in real time. No RPC polling. No delays. Just raw, first-access insight — the kind that wins trades before others even see them.

Here’s how you (the bot developer) can build a bot that listens to graph updates and fires before the rest of the network catches up:


Step 1: Implement a Hook

Your bot needs to implement the graph.Hook interface.
The validator will call methods on your Hook implementation as new updates arrive. This interface is how your bot observes slot roots and account state changes.

type Hook interface {
   Init(g graph.Graph) error
   OnSlot(slot graph.Slot, status graph.SlotStatus)
   CommitStart(slot graph.Slot)
   OnAccount(account graph.Account, isNew bool)
   CommitFinish()
}

Create a struct (e.g. myBot) that implements this interface. For example:

type myBot struct {
   ctx   context.Context // when you are done with bot kill this context
   graph graph.Graph
   em    *graph.EdgeManager
}

func (b *myBot) Init(g graph.Graph) error {
   go func() {
      <-b.ctx.Done()
      g.Cancel()
   }()

   b.graph = g
   b.em = g.EdgeManager()

   // Subscribe to Orca pool and wallet token accounts
   b.graph.Subscribe(b.ctx, <orcaPoolPubkey>, ^uint32(0), 2)
   b.graph.Subscribe(b.ctx, <walletTokenAccount>, ^uint32(0), 1)

   return nil
}

func (b *myBot) OnSlot(slot graph.Slot, status graph.SlotStatus) {
   // Handle slot timing if needed
}

func (b *myBot) CommitStart(slot graph.Slot) {
   // Start processing this slot
}

func (b *myBot) OnAccount(a graph.Account, isNew bool) {
   // React to changes — e.g., recheck price, balances, etc.
   if isNew {
      data := a.Data()
      // Check for price, balance, etc.
   }
}

func (b *myBot) CommitFinish() {
   // Submit tx if criteria are met
}

Step 2: Attach Your Hook to the CatScope Stream

The state.Client connects to the validator and calls your hook methods as new updates arrive.

ctx:= context.context // client context for entire program
client := state.New(ctx, dialer, retryInterval)
errorC := make(chan error, 1)

go client.DetachHook(errorC, &myBot{})

This automatically:

  • Opens a stream to the validator
  • Subscribes to account changes
  • Calls your Hook methods

Step 3: Handle Incoming Updates

Use the EdgeManager to explore connected edges and downstream accounts:

func (b *myBot) OnAccount(a graph.Account, isNew bool) {
      nodeId := a.Id()
      downstream := b.em.EdgeDown(nodeId)
      for childId := range downstream {
         childPubkey, ok := b.em.UnsafeNodeToPubkey(childId)
         if ok {
            childAccount := b.em.UnsafeAccount(childPubkey)
            // Inspect childAccount.Data() etc.
      }
   }
}

You can use EdgeDown (nodeId) to follow downstream connections from any account. This is useful when your logic depends on changes to connected state (e.g. tracking liquidity pool pairs).

Next steps

Once your bot is receiving and processing updates, the next step is:

  • Implement your algorithm: For example, calculate whether an arbitrage trade is profitable based on updated account data.
  • Send trades to the validator: Use the Catscope transaction gateway to submit transactions.