DEV Community

Cover image for Somnia On-Chain Reactivity
Neeraj Choubisa
Neeraj Choubisa Subscriber

Posted on

Somnia On-Chain Reactivity

Building Reactive Smart Contracts on Somnia
A Developer Guide with Real Solidity Examples

1. Introduction

As Web3 developers, we often build systems that require automatic reactions.

Examples from real projects like onchain games, PotWar pools, prediction markets:

  1. Last player wins when timer ends
  2. Auto reward after chest open
  3. Pool auto payout
  4. NFT level up after XP
  5. DAO proposal auto execute

With traditional smart contracts, this requires:

• Backend server
• Event indexer
• Cron jobs
• Extra user transactions

This increases complexity, cost, and centralization.

Somnia On-Chain Reactivity solves this by allowing smart contracts to automatically react to events fully on-chain.

2. What is Somnia On-Chain Reactivity?

Somnia lets smart contracts execute logic automatically when subscribed events occur.

Traditional Flow :

User Action → Event → Backend Listener → New Tx → State Update

Somnia Reactive Flow :

User Action → Event → Validator Detect → _onEvent() → State Update

No backend.
No extra user gas.
Fully decentralized.

3. Why This Matters for Web3 Devs

For builders working on gaming, DeFi, or automation:

  • No server infra
  • Less latency
  • Cleaner architecture
  • Better UX
  • Faster hackathon builds

For example, in Last Player Standing, we don’t need a backend timer contract logic can react automatically.

4. Example: Magic Chest Reactive Game

Let’s build a small game.

Game Rules

  • Common chest → +10 coins
  • Rare chest → +50 coins
  • Legendary chest → NFT sword

Player opens chest → reward auto given.

5. Solidity Contract Example

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

import { SomniaEventHandler }
from "@somnia-chain/reactivity-contracts/contracts/SomniaEventHandler.sol";

contract MagicChestReactiveGame is SomniaEventHandler {

    event ChestOpened(address indexed player, uint256 chestType);

    mapping(address => uint256) public coins;
    mapping(address => bool) public hasSword;

    bytes32 constant CHEST_SIG =
        keccak256("ChestOpened(address,uint256)");

    uint256 constant COMMON = 1;
    uint256 constant RARE = 2;
    uint256 constant LEGENDARY = 3;

    function openChest(uint256 chestType) external {
        emit ChestOpened(msg.sender, chestType);
    }

    function _onEvent(
        address,
        bytes32[] calldata topics,
        bytes calldata data
    ) internal override {

        require(topics[0] == CHEST_SIG, "Wrong event");

        address player =
            address(uint160(uint256(topics[1])));
        uint256 chestType =
            abi.decode(data, (uint256));

        if (chestType == COMMON)
            coins[player] += 10;
        else if (chestType == RARE)
            coins[player] += 50;
        else if (chestType == LEGENDARY)
            hasSword[player] = true;
    }
}
Enter fullscreen mode Exit fullscreen mode

6. How Reactivity Works Internally

  • Player calls openChest()
  • Event emitted
  • Somnia validators detect subscription
  • _onEvent() executes automatically
  • State updated
  • No extra transaction required.

Reactive tx is executed by validator address:

0x0000000000000000000000000000000000000100

7. Creating a Subscription for the event :

import { SDK } from "@somnia-chain/reactivity";
import { privateKeyToAccount } from "viem/accounts";
import { createPublicClient, createWalletClient, http } from "viem";
import { somniaTestnet } from "viem/chains";
import { keccak256, toBytes } from "viem";

const CONTRACT = "0xYourContractAddress";

async function main() {

  const account = privateKeyToAccount(
    process.env.PRIVATE_KEY as `0x${string}`
  );

  const publicClient = createPublicClient({
    chain: somniaTestnet,
    transport: http(),
  });

  const walletClient = createWalletClient({
    account,
    chain: somniaTestnet,
    transport: http(),
  });

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

  const EVENT_SIG = keccak256(
    toBytes("ChestOpened(address,uint256)")
  );

  const txHash = await sdk.createSoliditySubscription({
    handlerContractAddress: CONTRACT,
    emitter: CONTRACT,
    eventTopics: [EVENT_SIG],
    gasLimit: 3_000_000n,
  });

  const receipt = await publicClient.waitForTransactionReceipt({
    hash: txHash,
  });

  // 👉 Extract subscriptionId
  const log = receipt.logs[0];
  const subscriptionId = BigInt(log.topics[2]);

  console.log("Subscription ID:", subscriptionId.toString());
}

main();

Enter fullscreen mode Exit fullscreen mode

Important Notes

  • Need 32 STT balance
  • Save subscription ID
  • Event signature must match exactly

Now after subscribing event that we have to trigger from reactivity we need to test this ,

Testing Reactivity

After subscription, test contract.

import { ethers } from "hardhat";

const CONTRACT = "0xYourContractAddress";

async function main() {

  const [user] = await ethers.getSigners();

  const game = await ethers.getContractAt(
    "MagicChestReactiveGame",
    CONTRACT
  );

  const before = await game.coins(user.address);
  console.log("Coins before:", before.toString());

  const tx = await game.openChest(1);
  await tx.wait();

  console.log("Waiting for Somnia reactivity...");
  await new Promise(r => setTimeout(r,15000));

  const after = await game.coins(user.address);
  console.log("Coins after:", after.toString());

  if(after > before)
    console.log("✅ Reactivity Working");
  else
    console.log("❌ No Reactivity");
}

main();

Enter fullscreen mode Exit fullscreen mode

Real Use Cases

  1. Gaming

    • Last Player Standing
    • PotWar auto payout
    • Loot rewards
    • NFT upgrades
  2. Prediction Markets

    • Prediction auto reward
    • Score updates
  3. DeFi

    • Liquidations
    • Yield distribution
  4. DAO

    • Proposal auto execute

Conclusion

Somnia On-Chain Reactivity removes backend dependency and enables fully autonomous smart contracts.

For developers building automated gaming logic, prediction markets, or DeFi automation, this unlocks a new level of scalability and simplicity.

The future of Web3 apps is reactive.

Neeraj Choubisa (Nikku.Dev) is a Full-Stack Blockchain Engineer specializing in smart contract development, Web3 integrations, and consumer-focused decentralized applications.

🔗 Twitter

🔗 Github
🔗 LinkedIn

Top comments (0)