For the complete documentation index, see llms.txt. This page is also available as Markdown.

Off-Chain Reactivity: Filtered Subscriptions tutorial

This tutorial shows how to narrow an off-chain Reactivity subscription to a specific contract and event. It subscribes to ERC-20 Transfer logs and asks the node to run balanceOf(recipient) for each matching log, so the pushed notification contains both the event and the recipient's post-transfer balance.

For the lower-level protocol reference, see Off-chain Reactivity.

What You Will Build

The script will:

  1. Connect to a Somnia WebSocket RPC endpoint.

  2. Subscribe to Transfer(address,address,uint256) logs from one ERC-20.

  3. Use context: "topic3" to append the Transfer recipient to a balanceOf ethCall.

  4. Decode both the event log and the returned balance.

  5. Unsubscribe cleanly when the process exits.

Prerequisites

You'll need Node.js 20+.

Install the SDK and Viem:

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

Step 1: Set Up the SDK

This example uses Somnia mainnet. To use testnet, replace the chain ID, native currency and RPC URLs with the testnet values from Network Info.

Step 2: Build the Filter and Read-Only Call

The SDK fields map directly to the somnia_watch RPC parameters:

SDK field
RPC field
Meaning

eventContractSources

address

Emitters to watch.

topicOverrides

topics

Positional topic filter.

ethCalls

eth_calls

Read-only calls to run for each matching log.

context

context

Log fields to append to each call's calldata.

onlyPushChanges

push_changes_only

Drop events whose call results match the previous pushed event.

For ERC-20 Transfer, topics[2] is the recipient address. The off-chain Reactivity context selector is one-based, so topic3 appends topics[2] to the call data.

balanceOfSelector contains only the 4-byte function selector. The node appends the recipient address from the log because the subscription uses context: "topic3".

Step 3: Subscribe

onlyPushChanges compares the raw simulationResults with the previous pushed event on this subscription. It is useful when you care only about changed view results, but it is not a general event filter. For this recipient-balance example, leave it false unless you are comfortable dropping consecutive transfers whose recipients happen to have identical balances.

Step 4: Decode the Event and Balance

Call decodeTransferWithBalance(data.result) from onData.

Full Script

Save this as filtered.ts:

Run it:

Troubleshooting

  • Wrapped SOMI is not always actively transferred, so you may not see many events. You can pick another from Smart contracts or from the recent transactions on the block explorer.

  • If the subscription starts but decoding fails, confirm the ABI matches the event signature in topicOverrides.

Last updated