@somnia-chain/reactivity-contracts
v0.1.6
Published
Solidity NPM package for building event-driven applications on the Somnia blockchain
Downloads
463
Maintainers
Readme
@somnia-chain/reactivity-contracts
Solidity contracts and interfaces for building reactive smart contracts on the Somnia chain. This package enables on-chain reactivity: subscribe to events from other contracts and trigger automated responses via a the Reactivity precompile.
Key features:
- Abstract Handler: Inherit from
SomniaEventHandlerto easily implement event reactions with minimal boilerplate. - Precompile Interface: Interact with the Somnia Reactivity Precompile at
0x0100for subscription management. - Event Filtering: Fine-grained filters for topics, origins, callers, and emitters.
- Gas Management: Customizable fees and limits for efficient handling.
This powers event-driven smart contracts, like auto-rewards on transfers or real-time protocol updates. There is no ceiling.
Installation
npm install @somnia-chain/reactivity-contracts
# or
yarn add @somnia-chain/reactivity-contractsor for forge, install this repo with the forge install command.
Usage
Core Components
- SomniaEventHandler.sol: Abstract contract to inherit from. Override
_onEventfor your logic. Ensures only the precompile can invoke. - ISomniaEventHandler.sol: Interface for handler contracts.
- ISomniaReactivityPrecompile.sol: Interface for the precompile at
0x0100. - IERC165.sol: Standard interface detection.
Import in your Solidity files:
import { SomniaEventHandler } from "@somnia-chain/reactivity-contracts/contracts/SomniaEventHandler.sol";
import { ISomniaReactivityPrecompile } from "@somnia-chain/reactivity-contracts/contracts/interfaces/ISomniaReactivityPrecompile.sol";Creating a Handler Contract
Inherit from SomniaEventHandler and implement your reaction logic in _onEvent.
Example: Reward NFTs on large ERC20 transfers.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { SomniaEventHandler } from "@somnia-chain/reactivity-contracts/contracts/SomniaEventHandler.sol";
import { ISomniaReactivityPrecompile } from "@somnia-chain/reactivity-contracts/contracts/interfaces/ISomniaReactivityPrecompile.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
// Import your NFT contract or other dependencies
contract RewardHandler is SomniaEventHandler, Ownable {
address public tokenAddress; // ERC20 to monitor
uint256 public minTransferThreshold = 1000 ether; // Example threshold
constructor(address _tokenAddress) Ownable(msg.sender) {
tokenAddress = _tokenAddress;
}
function _onEvent(
address emitter,
bytes32[] calldata eventTopics,
bytes calldata data
) internal override {
// Decode Transfer event (assuming standard ERC20)
// topic[1] == from address (20 bytes padded to 32 bytes)
// topic[2] == to address (20 bytes padded to 32 bytes)
// abi.decode(data, (uint256 value));
// Example logic: If value > threshold, mint NFT to from
// Be cautious: Avoid reentrancy or loops
}
// Optional: Function to create subscription (fully on-chain)
function createSubscription() external onlyOwner {
ISomniaReactivityPrecompile precompile = ISomniaReactivityPrecompile(SomniaExtensions.SOMNIA_REACTIVITY_PRECOMPILE_ADDRESS);
ISomniaReactivityPrecompile.SubscriptionData memory subData = ISomniaReactivityPrecompile.SubscriptionData({
eventTopics: [keccak256("Transfer(address,address,uint256)"), bytes32(0), bytes32(0), bytes32(0)],
origin: address(0),
caller: address(0),
emitter: tokenAddress,
handlerContractAddress: address(this),
handlerFunctionSelector: this.onEvent.selector, // From ISomniaEventHandler
priorityFeePerGas: 10,
maxFeePerGas: 20,
gasLimit: 500_000,
isGuaranteed: true, // Regardless of block inclusion distance, always notify
isCoalesced: false // Never merge multiple notifications in a block into a single callback
});
precompile.subscribe(subData);
}
}- Security:
onEventchecksmsg.sender == 0x0100. Avoid emitting events that retrigger the handler. - IERC165 Support: Built-in for precompile detection by the nodes.
Managing Subscriptions
Use the precompile interface to subscribe/unsubscribe. Owners (EOA or contract) must hold min SOM (currently 32) for gas fees.
SubscriptionData Struct
struct SubscriptionData {
bytes32[4] eventTopics; // Filters (0x0 = wildcard)
address origin; // tx.origin filter (0x0 = wildcard)
address caller; // msg.sender filter (0x0 = wildcard)
address emitter; // Emitter filter (0x0 = wildcard)
address handlerContractAddress;
bytes4 handlerFunctionSelector; // Optional
uint64 priorityFeePerGas;
uint64 maxFeePerGas;
uint64 gasLimit;
bool isGuaranteed; // Retry if block full
bool isCoalesced; // Batch events
}Precompile Methods
subscribe(SubscriptionData): Returns subscriptionId.unsubscribe(uint64 subscriptionId).getSubscriptions(address owner): View owner's IDs.getSubscriptionInfo(uint64): View details.
Events:
SubscriptionCreated(uint64, address, SubscriptionData)SubscriptionRemoved(uint64, address)
Off-Chain Setup (via SDK)
Use @somnia-chain/reactivity SDK for wallet-based creation.
import { SDK } from '@somnia-chain/reactivity';
const sdk = new SDK({ /* config */ });
const subData = { /* Matching SubscriptionData */ };
await sdk.createSoliditySubscription(subData);Event Handling
On match, precompile calls your handler:
msg.sender:0x0100tx.origin: Subscription owner- Payload: Emitter, topics, data.
Docs
See here for more info from our official docs.
Repo Structure
/contracts/: SomniaEventHandler.sol/interfaces/: IERC165.sol, ISomniaEventHandler.sol, ISomniaReactivityPrecompile.sol
License
MIT - see LICENSE.
