@somnia-chain/reactivity-contracts
v0.2.0
Published
Solidity NPM package for building event-driven applications on the Somnia blockchain
Downloads
322
Maintainers
Readme
@somnia-chain/reactivity-contracts
Solidity contracts and interfaces for building reactive smart contracts on Somnia.
This package provides:
ISomniaReactivityPrecompile: precompile interface for subscription management.SomniaExtensions: helper library for creating and managing subscriptions.ISomniaEventHandlerstandard interface for event handlers.SomniaEventHandler: abstract base contract for secure callback handling.
Installation
npm install @somnia-chain/reactivity-contracts
# or
yarn add @somnia-chain/reactivity-contractsFor Foundry projects, install this repository with forge install.
Solidity Version
Contracts in this package use:
pragma solidity 0.8.30;Contract Overview
SomniaEventHandler
File: contracts/SomniaEventHandler.sol
- Implements
ISomniaEventHandlerandIERC165. - Exposes external
onEvent(address,bytes32[],bytes). - Restricts callback execution to the Somnia reactivity precompile at
address(0x0100). - Implements
supportsInterface(bytes4)(ERC-165) so the precompile can verify handler compatibility. - Reverts with
OnlyReactivityPrecompile()when called from an unauthorized address. - Requires child contracts to implement:
function _onEvent(
address emitter,
bytes32[] calldata eventTopics,
bytes calldata data
) internal virtual;ISomniaReactivityPrecompile
File: contracts/interfaces/ISomniaReactivityPrecompile.sol
Defines:
SubscriptionDatastruct.- System events:
BlockTick(uint64 indexed blockNumber),EpochTick(uint64 indexed epochNumber, uint64 indexed blockNumber),Schedule(uint256 indexed timestampMillis). - Subscription events:
SubscriptionCreated,SubscriptionRemoved. - Methods:
subscribe(SubscriptionData) → uint256 subscriptionIdunsubscribe(uint256 subscriptionId)getSubscriptionInfo(uint256 subscriptionId) → (SubscriptionData memory, address owner)
SomniaExtensions
File: contracts/interfaces/SomniaExtensions.sol
Library that wraps precompile interactions and provides ergonomic helpers.
Constants:
SOMNIA_REACTIVITY_PRECOMPILE_ADDRESS = address(0x0100)SUBSCRIPTION_OWNER_MINIMUM_BALANCE = 32 etherMINIMUM_BASE_FEE_PER_GAS = 6 gweiMAXIMUM_HANDLER_GAS_LIMIT = 200_000_000DEFAULT_PRIORITY_FEE_PER_GAS = 0DEFAULT_MAX_FEE_PER_GAS = 20 gweiDEFAULT_HANDLER_GAS_LIMIT = 10_000_000
Methods:
subscribe(address handler, SubscriptionFilter memory filter, SubscriptionOptions memory options) → uint256 subscriptionIdscheduleSubscriptionAtTimestamp(address handler, uint256 timestampMillis, SubscriptionOptions memory options) → uint256 subscriptionIdscheduleSubscriptionAtBlock(address handler, uint64 blockNumber, SubscriptionOptions memory options) → uint256 subscriptionIdscheduleSubscriptionAtEpoch(address handler, uint64 epochNumber, SubscriptionOptions memory options) → uint256 subscriptionIdunsubscribe(uint256 subscriptionId)getSubscriptionInfo(uint256 subscriptionId) → (SubscriptionData memory, address owner)defaultSubscriptionOptions() → SubscriptionOptions memory— returns the library defaults
Helper structs:
SubscriptionFilter { bytes32[4] eventTopics, address origin, address emitter }SubscriptionOptions { uint64 priorityFeePerGas, uint64 maxFeePerGas, uint64 gasLimit }
Errors:
HandlerZeroAddress()— handler address is zeroEmptyFilter()— all filter fields are wildcardsGasLimitZero()—options.gasLimitis 0GasLimitExceeded()—options.gasLimitexceedsMAXIMUM_HANDLER_GAS_LIMITInvalidMaxFeePerGas()—maxFeePerGasis non-zero but less thanpriorityFeePerGas + MINIMUM_BASE_FEE_PER_GASInsufficientBalance()— calling contract balance is belowSUBSCRIPTION_OWNER_MINIMUM_BALANCETimestampInPast()— requested timestamp is not in the futureBlockInPast()— requested block number is not in the futureUnsubscribeFailed()— low-level call to the precompile'sunsubscribereverted
Usage
1. Implement a Handler
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
import {SomniaEventHandler} from "@somnia-chain/reactivity-contracts/contracts/SomniaEventHandler.sol";
contract MyHandler is SomniaEventHandler {
function _onEvent(
address emitter,
bytes32[] calldata eventTopics,
bytes calldata data
) internal override {
// Implement your reaction logic here.
// onEvent is already protected so only the precompile can call it.
emitter;
eventTopics;
data;
}
}2. Create Subscriptions with SomniaExtensions
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
import {SomniaExtensions} from "@somnia-chain/reactivity-contracts/contracts/interfaces/SomniaExtensions.sol";
contract SubscriptionManager {
function createTransferSubscription(address handler, address token)
external
returns (uint256)
{
SomniaExtensions.SubscriptionFilter memory filter = SomniaExtensions.SubscriptionFilter({
eventTopics: [
keccak256("Transfer(address,address,uint256)"),
bytes32(0),
bytes32(0),
bytes32(0)
],
origin: address(0),
emitter: token
});
SomniaExtensions.SubscriptionOptions memory options = SomniaExtensions.SubscriptionOptions({
priorityFeePerGas: 1 gwei,
maxFeePerGas: 10 gwei,
gasLimit: 10_000_000
});
return SomniaExtensions.subscribe(handler, filter, options);
}
}Security Notes
SomniaEventHandler.onEventenforcesmsg.sender == address(0x0100)viaSOMNIA_REACTIVITY_PRECOMPILE_ADDRESS.SomniaExtensionsvalidates handler address, filter presence, gas limits, fee configuration, and minimum owner balance before subscribing.- Ensure your
_onEventlogic is safe against reentrancy and unintended side effects.
Repository Structure
contracts/SomniaEventHandler.solcontracts/interfaces/IERC165.solcontracts/interfaces/ISomniaEventHandler.solcontracts/interfaces/ISomniaReactivityPrecompile.solcontracts/interfaces/SomniaExtensions.sol
Documentation
Official docs: https://docs.somnia.network/developer/reactivity
Testing
Tests are located in the test/ directory and can be run with Foundry:
forge testAdditional live testing can be done on the Somnia testnet using the provided script:
./test/LiveTestSomniaExtensions.sh https://your-rpc-url 0xYOUR_PRIVATE_KEYLicense
MIT - see LICENSE
