@somnia-chain/reactivity
v0.2.1
Published
Typescript SDK to build event-driven applications on the Somnia blockchain
Maintainers
Readme
Somnia Reactivity TypeScript SDK
The Somnia Reactivity TypeScript SDK enables applications within the JavaScript and TypeScript ecosystem to immediately utilize reactivity tooling offered by Somnia chain with minimal boilerplate code.
Key abstractions
The SDK uses viem to abstract away RPC calls, wallets, transaction creation, and starting subscriptions.
- For read-only access, provide just a public client — the SDK handles contract setup for Somnia testnet or mainnet.
- For transactions, add any wallet client: private key-based, remote signer, ERC-4337 AA, etc.
Getting Started
Installation
npm i @somnia-chain/reactivityPlugging into the SDK
You'll need viem installed for the public and/or wallet client. Install it with npm i viem.
import { createPublicClient, createWalletClient, http, defineChain } from 'viem'
import { SDK } from '@somnia-chain/reactivity'
const chain = defineChain(/* see viem docs */)
const publicClient = createPublicClient({
chain,
transport: http(),
})
// Optional: wallet client for writes
const walletClient = createWalletClient({
account,
chain,
transport: http(),
})
const sdk = new SDK({
public: publicClient,
wallet: walletClient, // omit if not executing transactions on-chain
})WebSocket Watch (off-chain)
Use WebSocket subscriptions for real-time event and state updates delivered atomically. The client must be created with a WebSocket transport.
import { SDK, WebsocketSubscriptionInitParams, RpcResponse, SubscriptionCallback } from '@somnia-chain/reactivity'
const initParams: WebsocketSubscriptionInitParams = {
ethCalls: [], // view calls to execute on each event
context: 'data', // optional: 'topic0', 'data', 'address', …
onData: (notification: RpcResponse<SubscriptionCallback>) => {
const result = notification.params?.result
console.log('topics:', result?.topics)
console.log('data:', result?.data)
console.log('simulationResults:', result?.simulationResults)
},
onError: (error) => console.error('Error:', error), // optional
eventContractSources: ['0xYourEmitterAddress'], // optional: filter by emitter
topicOverrides: ['0xYourEventSelector'], // optional: filter by topic
onlyPushChanges: true, // optional: skip unchanged state
}
const subscription = await sdk.watch(initParams)
if (subscription instanceof Error) throw subscription
// Later, unsubscribe when done
await subscription.unsubscribe()On-Chain Subscriptions
The SDK exposes methods aligned with the Solidity SomniaExtensions ergonomics:
subscribe({ handlerContractAddress, filter?, options })— subscribe to contract eventsscheduleSubscriptionAtTimestamp({ timestampMs, handlerContractAddress, options })— trigger at an absolute Unix timestamp (ms)scheduleSubscriptionAtBlock({ blockNumber?, handlerContractAddress, options })— trigger at a specific (or any future) blockscheduleSubscriptionAtEpoch({ epochNumber, handlerContractAddress, options })— trigger at a specific epochunsubscribe(subscriptionId)— cancel an active subscriptiongetSubscriptionInfo(subscriptionId)— fetch parameters and owner of a subscriptionsubscribeRaw(subscriptionData)— low-level wrapper accepting a fully-assembledSoliditySubscriptionDatastruct
Low-level precompile fields are managed internally and intentionally hidden from subscribe inputs: caller, isGuaranteed, isCoalesced.
import { SDK, SoliditySubscribeRequest } from '@somnia-chain/reactivity'
const request: SoliditySubscribeRequest = {
handlerContractAddress: '0xYourHandlerAddress',
filter: {
eventTopics: ['0xYourEventSelector'], // optional
emitter: '0xYourEmitterAddress', // optional
},
options: {
priorityFeePerGas: 1_000_000_000n,
maxFeePerGas: 10_000_000_000n,
gasLimit: 5_000_000n,
},
}
const txHash = await sdk.subscribe(request)
if (txHash instanceof Error) throw txHashCancelling a subscription
const txHash = await sdk.unsubscribe(subscriptionId) // subscriptionId: bigint
if (txHash instanceof Error) throw txHash
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash })Querying subscription details
import { SDK, SoliditySubscriptionInfo } from '@somnia-chain/reactivity'
const info = await sdk.getSubscriptionInfo(subscriptionId) // subscriptionId: bigint
if (info instanceof Error) throw info
// info.subscriptionData — full SubscriptionData struct
// info.owner — address that owns the subscriptionExported Types and Constants
| Export | Description |
|---|---|
| SoliditySubscribeRequest | Input type for sdk.subscribe() |
| SoliditySubscriptionData | Low-level precompile subscription struct (used by subscribeRaw) |
| SoliditySubscriptionInfo | Return type of sdk.getSubscriptionInfo() — { subscriptionData, owner } |
| SoliditySubscriptionFilter | { eventTopics?, origin?, emitter? } |
| SoliditySubscriptionOptions | { priorityFeePerGas, maxFeePerGas, gasLimit } |
| BlockTickSubscriptionRequest | Input type for sdk.scheduleSubscriptionAtBlock() |
| ScheduleRequest | Input type for sdk.scheduleSubscriptionAtTimestamp() |
| EpochTickSubscriptionRequest | Input type for sdk.scheduleSubscriptionAtEpoch() |
| WebsocketSubscriptionInitParams | Input type for sdk.watch() |
| SubscriptionCallback | Shape of notification.params.result inside the onData callback |
| RpcResponse | Full notification envelope passed to the onData callback |
| EthCall | Single ETH call entry used in WebsocketSubscriptionInitParams.ethCalls |
| defaultSubscriptionOptions | Default fee/gas values (priorityFeePerGas: 0, maxFeePerGas: 20 gwei, gasLimit: 10_000_000) |
Live Test
An end-to-end test runner lives in sdk/example/live-test.mjs. It exercises all public methods against a real node:
watchsubscribeRawsubscribescheduleSubscriptionAtTimestampscheduleSubscriptionAtBlockscheduleSubscriptionAtEpochgetSubscriptionInfounsubscribe
Run it via npm script (builds the SDK first):
# from sdk/example/
npm run live-test -- <rpc-url> <private-key>
# from repo root
npm run example:sdk:live -- <rpc-url> <private-key>
# or via the shell wrapper
./sdk/example/live-test.sh <rpc-url> <private-key>Optionally set SOMNIA_LIVE_ENABLE_WATCH=1 to include the WebSocket smoke test (skipped by default as it requires a WebSocket-capable endpoint).
Documentation
For detailed documentation, head over to our doc site here.
