Somnia On-Chain 反应性

发布: (2026年2月16日 GMT+8 22:06)
6 分钟阅读
原文: Dev.to

Source: Dev.to

Somnia 链上反应性封面图

Neeraj Choubisa

1. 引言

作为 Web3 开发者,我们经常构建需要自动响应的系统。
真实项目中的例子包括链上游戏、PotWar 池、预测市场:

  • 计时结束时最后一位玩家获胜
  • 开箱后自动奖励
  • 池子自动支付
  • NFT 在获得 XP 后升级
  • DAO 提案自动执行

使用传统智能合约,这需要:

  • 后端服务器
  • 事件索引器
  • 定时任务(Cron jobs)
  • 额外的用户交易

这会增加复杂性、成本和中心化风险。

Somnia 链上响应性 通过允许智能合约在链上完全自动响应事件来解决这些问题。

2. 什么是 Somnia 链上响应性?

Somnia 让智能合约在订阅的事件发生时自动执行逻辑。

传统流程

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

Somnia 响应式流程

User Action → Event → Validator Detect → _onEvent() → State Update
  • 无后端。
  • 无额外用户 gas。
  • 完全去中心化。

3. 为什么这对 Web3 开发者很重要

对于从事游戏、DeFi 或自动化的构建者:

  • 无服务器基础设施
  • 更低延迟
  • 更简洁的架构
  • 更好的用户体验
  • 更快的黑客松构建

例如,在 Last Player Standing 中我们不需要后端计时器;合约逻辑可以自动响应。

4. 示例:Magic Chest Reactive Game

让我们构建一个小型游戏。

游戏规则

  • Common chest → +10 金币
  • Rare chest → +50 金币
  • Legendary chest → NFT 剑

玩家打开箱子 → 奖励自动发放。

5. Solidity 合约示例

// 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;
    }
}

6. Reactivity 工作原理(内部)

  1. 玩家调用 openChest()
  2. 事件被触发
  3. Somnia 验证者检测到订阅
  4. _onEvent() 自动执行
  5. 状态已更新
  6. 无需额外交易

Reactive transaction is executed by validator address:

0x0000000000000000000000000000000000000100

7. 为事件创建订阅

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();

重要说明

  • 需要 32 STT 余额。
  • 保存订阅 ID。
  • 事件签名必须完全匹配。

8. 测试响应性

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); // 1 = COMMON
  await tx.wait();

  console.log("Waiting for Somnia reactivity...");
  await new Promise(r => setTimeout(r, 5000)); // adjust as needed

  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();

9. 实际使用案例

游戏

  • 最后存活玩家
  • PotWar 自动支付
  • 掠夺奖励
  • NFT 升级

预测市场

  • 自动奖励分配
  • 分数更新

去中心化金融 (DeFi)

  • 清算
  • 收益分配

DAO

  • 提案自动执行

10. 结论

Somnia On‑Chain Reactivity 消除了对后端的依赖,使智能合约能够完全自治。

对于构建自动化游戏逻辑、预测市场或 DeFi 自动化的开发者而言,这开启了全新的可扩展性和简易性。

Web3 应用的未来是响应式的。

Neeraj Choubisa(Nikku.Dev)— 全栈区块链工程师,专注于智能合约开发、Web3 集成以及面向消费者的去中心化应用。

0 浏览
Back to Blog

相关文章

阅读更多 »

编排到底是什么?

引言 我在职业生涯开始时意外进入了流程自动化和编排领域,而某种程度上——更意外的是——我仍然……