> ## Documentation Index
> Fetch the complete documentation index at: https://partner.tren.ch/llms.txt
> Use this file to discover all available pages before exploring further.

# CPI event migration

> Migrate a Trench indexer from program logs to CPI events.

Trench and Trench Governance now emit Anchor events through CPI. Partner API request and response schemas, endpoints, transaction signatures, token balance changes, and confirmation behavior are unchanged.

## Indexer changes

Subscribe to Trench and governance transaction updates through your gRPC and inspect `meta.innerInstructions` from each update.

For each inner instruction:

1. Resolve its program ID through the transaction's static and loaded account keys.
2. Keep only Trench or governance instructions.
3. Verify the first instruction account is that program's `__event_authority` PDA.
4. Read the instruction data bytes from the gRPC update.
5. Validate the CPI event tag.
6. Decode the remaining event discriminator and Borsh payload with SDK `0.5.0` or the matching IDL.

```ts theme={null}
import type { SubscribeUpdate } from "@triton-one/yellowstone-grpc";
import { PublicKey } from "@solana/web3.js";
import {
  parseEventCpiData,
  parseGovernanceEventCpiData,
} from "@trench-prod/trench-program-sdk/events";
import { findEventAuthorityPda } from "@trench-prod/trench-program-sdk/pda";

const TRENCH = new PublicKey("4yjQUVLV4VPzgGCXPGBQUziGjo1HG2atxsBBsS3jddHB");
const GOVERNANCE = new PublicKey("Fmy9Mi1RbcHPEht6ydGmrLGFCiU3P9JSUomVDEsYwF2J");

export function decodeCpiEvents(update: SubscribeUpdate) {
  const info = update.transaction?.transaction;
  const message = info?.transaction?.message;
  const meta = info?.meta;
  if (!message || !meta) return [];

  const keys = [
    ...(message.accountKeys ?? []),
    ...(meta.loadedWritableAddresses ?? []),
    ...(meta.loadedReadonlyAddresses ?? []),
  ].map((key) => new PublicKey(key));
  const events = [];

  for (const group of [...(meta.innerInstructions ?? [])].sort((a, b) => a.index - b.index)) {
    for (const ix of group.instructions) {
      const programId = keys[ix.programIdIndex];
      if (!programId || (!programId.equals(TRENCH) && !programId.equals(GOVERNANCE))) continue;

      const authorityIndex = ix.accounts[0];
      const expectedAuthority = findEventAuthorityPda(programId)[0];
      if (authorityIndex === undefined || !keys[authorityIndex]?.equals(expectedAuthority)) {
        throw new Error("invalid CPI event authority");
      }

      const event = programId.equals(TRENCH)
        ? parseEventCpiData(ix.data)
        : parseGovernanceEventCpiData(ix.data);
      if (!event) throw new Error("invalid CPI event payload");
      events.push({ outerInstructionIndex: group.index, event });
    }
  }

  return events;
}
```
