# Activity feeds

Confidential tokens emit standard events (transfers, wraps, unwraps) but the amounts are encrypted handles. The SDK provides utilities to fetch these logs, classify them by type and direction, decrypt the amounts, and produce a feed ready for your UI.

## Steps

### 1. Fetch logs from the chain

Start by querying event logs from the token contract. The SDK exports `TOKEN_TOPICS` -- an array of topic hashes covering all relevant events (confidential transfers, wraps, and unwraps).

{% tabs %}
{% tab title="viem" %}

```ts
import { TOKEN_TOPICS } from "@zama-fhe/sdk";

const logs = await publicClient.getLogs({
  address: tokenAddress,
  topics: [TOKEN_TOPICS],
  fromBlock: deployBlock,
  toBlock: "latest",
});
```

{% endtab %}
{% endtabs %}

You can narrow the block range to limit the number of logs returned. For a production app, you would typically paginate or use an indexer.

### 2. Parse raw logs with parseActivityFeed

Pass the raw logs and the user's address to `parseActivityFeed`. It classifies each event by type (`ConfidentialTransfer`, `Wrapped`, `UnwrapRequested`, etc.) and direction (`incoming`, `outgoing`, or `self`).

{% tabs %}
{% tab title="SDK" %}

```ts
import { parseActivityFeed } from "@zama-fhe/sdk";

const items = parseActivityFeed(logs, userAddress);
// Each item has: .type, .direction, .from, .to, .encryptedHandle, .blockNumber
```

{% endtab %}
{% endtabs %}

### 3. Extract encrypted handles

The parsed items contain encrypted handles that need decrypting before you can show amounts. Use `extractEncryptedHandles` to collect them into a flat array.

{% tabs %}
{% tab title="SDK" %}

```ts
import { extractEncryptedHandles } from "@zama-fhe/sdk";

const handles = extractEncryptedHandles(items);
```

{% endtab %}
{% endtabs %}

### 4. Decrypt the handles

Decrypt all handles in one batch call via `sdk.userDecrypt`. Each handle must be paired with its contract address (the token contract that emitted it). The result is passed straight to `applyDecryptedValues` in the next step.

{% tabs %}
{% tab title="SDK" %}

```ts
const decrypted = await sdk.userDecrypt(
  handles.map((handle) => ({ handle, contractAddress: tokenAddress })),
);
```

{% endtab %}
{% endtabs %}

This call requires FHE credentials. If the user has not signed yet, the SDK prompts for a wallet signature. See [Check Balances](/protocol/sdk/guides/check-balances.md) for details on the signature flow.

### 5. Attach decrypted values with applyDecryptedValues

Merge the decrypted amounts back into the activity items. Each item gains an `.amount` field with the human-readable value.

{% tabs %}
{% tab title="SDK" %}

```ts
import { applyDecryptedValues } from "@zama-fhe/sdk";

const enrichedItems = applyDecryptedValues(items, decryptedMap);
// Each item now has .amount (bigint) alongside .encryptedHandle
```

{% endtab %}
{% endtabs %}

### 6. Sort and display

Use `sortByBlockNumber` to order the feed newest-first, then render it in your UI.

{% tabs %}
{% tab title="SDK" %}

```ts
import { sortByBlockNumber } from "@zama-fhe/sdk";

const feed = sortByBlockNumber(enrichedItems);

feed.forEach((item) => {
  console.log(`${item.type} | ${item.direction} | ${item.amount}`);
});
```

{% endtab %}
{% endtabs %}

Here is the full pipeline in one block for reference:

{% tabs %}
{% tab title="Full example" %}

```ts
import {
  parseActivityFeed,
  extractEncryptedHandles,
  applyDecryptedValues,
  sortByBlockNumber,
} from "@zama-fhe/sdk";

// 1. Parse raw logs into classified items
const items = parseActivityFeed(logs, userAddress);

// 2. Pull out handles that need decrypting
const handles = extractEncryptedHandles(items);

// 3. Decrypt the handles
const decrypted = await sdk.userDecrypt(
  handles.map((handle) => ({ handle, contractAddress: tokenAddress })),
);

// 4. Attach the decrypted amounts
const enrichedItems = applyDecryptedValues(items, decrypted);

// 5. Sort newest first
const feed = sortByBlockNumber(enrichedItems);
```

{% endtab %}
{% endtabs %}

### 7. Use the useActivityFeed hook in React

The React SDK wraps the entire pipeline into a single hook. Pass it your logs and it handles parsing, decryption, and sorting automatically.

{% tabs %}
{% tab title="React" %}

```tsx
import { useActivityFeed } from "@zama-fhe/react-sdk";

const { data: feed, isLoading } = useActivityFeed({
  tokenAddress: "0xToken",
  logs,
  userAddress,
  decrypt: true,
});

feed?.forEach((item) => {
  console.log(item.type, item.direction, item.amount);
});
```

{% endtab %}
{% endtabs %}

When `decrypt` is `true`, the hook decrypts all handles and attaches amounts. Set it to `false` if you only need the classified event metadata without decrypted values.

You can control cache invalidation with `zamaQueryKeys.activityFeed`:

```tsx
import { zamaQueryKeys } from "@zama-fhe/react-sdk";
import { useQueryClient } from "@tanstack/react-query";

const queryClient = useQueryClient();
queryClient.invalidateQueries({
  queryKey: zamaQueryKeys.activityFeed.token("0xToken"),
});
```

## Next steps

* See [Event Decoders](https://github.com/zama-ai/sdk/blob/main/reference/sdk/event-decoders/README.md) for the event decoder API (`decodeOnChainEvents`, `TOKEN_TOPICS`, individual decoders).
* See [Hooks](https://github.com/zama-ai/sdk/blob/main/reference/react/query-keys/README.md) for `useActivityFeed` details and query key reference.
* To handle decryption errors during feed building, see [Handle Errors](/protocol/sdk/guides/handle-errors.md).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.zama.org/protocol/sdk/guides/activity-feeds.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
