# Wildcard Off-Chain Reactivity Tutorial

This tutorial shows how to create an off-chain Reactivity subscription from TypeScript using the `@somnia-chain/reactivity` SDK. A wildcard subscription omits both address and topic filters, so the node pushes every new log it sees on the WebSocket connection.

Wildcard subscriptions are useful for quick testing and exploratory scripts. Production applications should usually add filters, as shown in the [filtered subscriptions tutorial](/developer/reactivity/tutorials/off-chain-reactivity-filtered-subscriptions-tutorial.md).

Off-chain subscriptions:

* live only for the WebSocket connection that created them
* are not stored on-chain
* do not require a wallet client or gas
* can include `ethCalls` whose raw return data is delivered with each event

For the full protocol reference, see [Off-chain Reactivity](/developer/reactivity/reactivity-offchain.md).

## Prerequisites

You'll need Node.js 20+.

Install the SDK and Viem:

```bash
npm install @somnia-chain/reactivity viem
npm install --save-dev tsx typescript @types/node
```

## Step 1: Define the Chain

This example uses Somnia Testnet. You can switch to mainnet by changing the chain ID, native currency and RPC URLs to the mainnet values from [Network Info](/developer/network-info.md).

```typescript
import { defineChain } from 'viem';

const somniaTestnet = defineChain({
  id: 50312,
  name: 'Somnia Testnet',
  nativeCurrency: {
    decimals: 18,
    name: 'STT',
    symbol: 'STT',
  },
  rpcUrls: {
    default: {
      http: ['https://api.infra.testnet.somnia.network'],
      webSocket: ['wss://api.infra.testnet.somnia.network/ws'],
    },
  },
});
```

## Step 2: Create the SDK

Off-chain reactivity requires a public client with a WebSocket transport. You do not need a wallet client because no transaction is signed.

```typescript
import { SDK } from '@somnia-chain/reactivity';
import { createPublicClient, webSocket } from 'viem';

const publicClient = createPublicClient({
  chain: somniaTestnet,
  transport: webSocket(somniaTestnet.rpcUrls.default.webSocket[0]),
});

const sdk = new SDK({ public: publicClient });
```

## Step 3: Subscribe to All Logs

Pass `ethCalls: []` when you only want log notifications. Omitting `eventContractSources` and `topicOverrides` makes the subscription wildcard.

```typescript
const subscription = await sdk.subscribe({
  ethCalls: [],
  onData: (data) => {
    const event = data.result;

    console.log('Emitter:', event.address);
    console.log('Topics:', event.topics);
    console.log('Data:', event.data);
    console.log('Simulation results:', event.simulationResults);
  },
  onError: (error) => {
    console.error('Subscription error:', error);
  },
});

if (subscription instanceof Error) {
  throw subscription;
}
```

Each notification's event payload contains:

| Field               | Meaning                                                |
| ------------------- | ------------------------------------------------------ |
| `address`           | Contract address that emitted the log.                 |
| `topics`            | Event topics, with the event signature at `topics[0]`. |
| `data`              | ABI-encoded non-indexed event data.                    |
| `simulationResults` | Raw return data from configured `ethCalls`, in order.  |

## Step 4: Decode a Known Event

A wildcard subscription receives many different event types. Decode only logs whose signature matches the ABI you are using. This example recognises ERC-20 `Transfer` logs.

```typescript
import {
  decodeEventLog,
  erc20Abi,
  toEventSelector,
} from 'viem';

const transferTopic = toEventSelector('Transfer(address,address,uint256)');

function handleEvent(event: {
  address: `0x${string}`;
  topics: `0x${string}`[];
  data: `0x${string}`;
  simulationResults: `0x${string}`[];
}) {
  if (event.topics[0] !== transferTopic) {
    return;
  }

  const decoded = decodeEventLog({
    abi: erc20Abi,
    topics: event.topics,
    data: event.data,
  });

  console.log('ERC-20 Transfer:', decoded.args);
}
```

Then call `handleEvent(data.result)` inside `onData`.

## Full Script

Save this as `main.ts`:

```typescript
import { SDK } from '@somnia-chain/reactivity';
import {
  createPublicClient,
  decodeEventLog,
  defineChain,
  erc20Abi,
  toEventSelector,
  webSocket,
} from 'viem';

const somniaTestnet = defineChain({
  id: 50312,
  name: 'Somnia Testnet',
  nativeCurrency: {
    decimals: 18,
    name: 'STT',
    symbol: 'STT',
  },
  rpcUrls: {
    default: {
      http: ['https://api.infra.testnet.somnia.network'],
      webSocket: ['wss://api.infra.testnet.somnia.network/ws'],
    },
  },
});

const transferTopic = toEventSelector('Transfer(address,address,uint256)');

const publicClient = createPublicClient({
  chain: somniaTestnet,
  transport: webSocket(somniaTestnet.rpcUrls.default.webSocket[0]),
});

const sdk = new SDK({ public: publicClient });

async function main() {
  const subscription = await sdk.subscribe({
    ethCalls: [],
    onData: (data) => {
      const event = data.result;

      console.log('Raw event:', event);

      if (event.topics[0] !== transferTopic) {
        return;
      }

      const decoded = decodeEventLog({
        abi: erc20Abi,
        topics: event.topics,
        data: event.data,
      });

      console.log('Decoded ERC-20 Transfer:', decoded.args);
    },
    onError: (error) => {
      console.error('Subscription error:', error);
    },
  });

  if (subscription instanceof Error) {
    throw subscription;
  }

  console.log('Subscribed:', subscription.subscriptionId);

  process.on('SIGINT', async () => {
    await subscription.unsubscribe();
    process.exit(0);
  });
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});
```

Run it:

```bash
npx tsx main.ts
```

## Next Steps

Add `eventContractSources` and `topicOverrides` once you know which contracts and events you want. Add `ethCalls` when you want the node to attach read-only simulation results to each pushed event.


---

# 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.somnia.network/developer/reactivity/tutorials/wildcard-off-chain-reactivity-tutorial.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.
