bitpro가 원문에서 글자만 모은 내용이에요. 표·그림·수식은 빠지거나 깨질 수 있어요. 전체 54쪽 중 6쪽
p with `slot -> value` entries |
| `stateDiff` | `object` or `null` | Patch individual storage slots with `slot -> value` entries |
> `state` and `stateDiff` are mutually exclusive. Providing both returns an error.
Several methods take similar log-filter-shaped inputs, but they are not one shared request type in source.
### Response Schemas
**Transaction** (returned by [`eth_getTransactionByHash`](#eth_gettransactionbyhash) and similar):
| Field | Type | Notes |
| ---------------------- | -------------------- | --------------------------------------------------------------- |
| `blockHash` | `hash` | |
| `blockNumber` | `quantity` | |
| `from` | `address` | |
| `to` | `address` or `null` | `null` for contract creation |
| `gas` | `quantity` | Gas limit |
| `gasPrice` | `quantity` | Effective gas price |
| `hash` | `hash` | Transaction hash |
| `input` | `bytes` | Calldata (`"0x"` for simple transfers) |
| `nonce` | `quantity` | |
| `transactionIndex` | `quantity` | Position within the block |
| `value` | `quantity` | Wei transferred |
| `type` | `quantity` | `0x0` legacy, `0x1` access list, `0x2` EIP-1559, `0x4` EIP-7702 |
| `chainId` | `quantity` | |
| `v`, `r`, `s` | `quantity` | ECDSA signature components |
| `maxPriorityFeePerGas` | `quantity` or `null` | Present for type `0x2` and `0x4` only |
| `maxFeePerGas` | `quantity` or `null` | Present for type `0x2` and `0x4` only |
| `authorizationList` | `array` or `null` | Present for type `0x4` only |
**TransactionReceipt** (returned by [`eth_getTransactionReceipt`](#eth_gettransactionreceipt) and similar):
| Field | Type | Notes |
| ------------------- | ------------------- | -------------------------------------- |
| `transactionHash` | `hash` | |
| `transactionIndex` | `quantity` | |
| `blockHash` | `hash` | |
| `blockNumber` | `quantity` | |
| `from` | `address` | |
| `to` | `address` or `null` | `null` for contract creation |
| `cumulativeGasUsed` | `quantity` | |
| `gasUsed` | `quantity` | Gas consumed by this transaction |
| `contractAddress` | `address` or `null` | Address of created contract, or `null` |
| `type` | `quantity` | Transaction type |
| `status` | `quantity` | `0x1` success, `0x0` revert |
| `logsBloom` | `bytes` | 256-byte bloom filter |
| `logs` | [`Log`](#log)`[]` | Array of log entries (see below) |
| `effectiveGasPrice` | `quantity` | Actual gas price paid |
**Log** (entries within `logs` arrays):
| Field | Type | Notes |
| ------------------ | ---------- | ------------------------------------------------------------------------- |
| `address` | `address` | Contract that emitted the event |
| `topics` | `hash[]` | Up to 4 indexed event parameters; `topics[0]` is the event signature hash |
| `data` | `bytes` | ABI-encoded non-indexed parameters |
| `blockNumber` | `quantity` | |
| `transactionHash` | `hash` | |
| `transactionIndex` | `quantity` | |
| `blockHash` | `hash` | |
| `logIndex` | `quantity` | Position within the block's logs |
**Block** (returned by [`eth_getBlockByNumber`](#eth_getblockbynumber) and similar):
| Field | Type | Notes |
| ------------------ | --------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `hash` | `hash` | |
| `parentHash` | `hash` | |
| `sha3Uncles` | `hash` | Always the empty uncles hash (no uncle blocks) |
| `miner` | `address` | Always `0x0000...0000` |
| `stateRoot` | `hash` | |
| `transactionsRoot` | `hash` | |
| `receiptsRoot` | `hash` | |
| `logsBloom` | `bytes` | 256-byte bloom filter |
| `difficulty` | `quantity` | Always `0x0` |
| `number` | `quantity` | Block number |
| `gasLimit` | `quantity` | Per-block execution gas |
| `gasUsed` | `quantity` | |
| `baseFeePerGas` | `quantity` | EIP-1559 base fee |
| `timestamp` | `quantity` | Unix timestamp |
| `size` | `quantity` | RLP-encoded block size in bytes |
| `extraData` | `bytes` | |
| `mixHash` | `hash` | Always zero |
| `nonce` | `bytes` | Always zero (8 bytes) |
| `transactions` | `hash[]` or [`Transaction`](#transaction)`[]` | Tx hashes if `fullTxObjects=false`, full objects if `true`. Omitted entirely for `eth_getHeaderBy*`. |
**SomniaLedgerBlock** (returned by [`somnia_getBlockByNumber`](#somnia_getblockbynumber) / [`somnia_getBlockByHash`](#somnia_getblockbyhash)):
| Field | Type | Notes |
| -------------------------- | ----------------------------------------------- | -------------------------- |
| `consensus_block` | [`SomniaConsensusBlock`](#somniaconsensusblock) | Consensus-layer block data |
| `execution_block` | [`SomniaExecutionBlock`](#somniaexecutionblock) | Execution-layer block data |
| `parent_ledger_block_hash` | `hash` | Parent ledger-block hash |
| `ledger_block_hash` | `hash` | Ledger-block hash |
**SomniaConsensusBlock**:
| Field | Type | Notes |
| ----------------------------- | ----------------------------------------------------- | ------------------------------ |
| `block_number` | `quantity` | Ledger block number |
| `timestamp` | `quantity` | Unix timestamp in milliseconds |
| `data_chain_blocks` | `hash[]` | Referenced data-chain blocks |
| `delayed_ledger_block_number` | `quantity` | Delayed ledger block number |
| `delayed_ledger_block_hash` | `hash` | Delayed ledger block hash |
| `parent_consensus_block_hash` | `hash` | Parent consensus-block hash |
| `proposer_address` | `address` | Block proposer |
| `block_resources` | [`SomniaFrontendResources`](#somniafrontendresources) | Frontend resource accounting |
| `consensus_block_hash` | `hash` | Consensus-block hash |
**SomniaExecutionBlock**:
| Field | Type | Notes |
| ----------------------------- | ---------- | --------------------------------------------- |
| `transaction_ids_hash` | `hash` | Hash of transaction ids |
| `receipts_hash` | `hash` | Hash of receipts |
| `execution_gas_limit` | `quantity` | Execution gas limit |
| `execution_gas_used` | `quantity` | Execution gas used |
| `execution_state_snapshot` | `hash` | State snapshot hash |
| `state_snapshot_block_number` | `quantity` | Block number of the state snapshot |
| `operation_sequence_hash` | `quantity` | Operation sequence hash encoded as a quantity |
**SomniaFrontendResources**:
| Field | Type | Notes |
| -------------------- | ---------- | ------------------------------------------ |
| `validation_gas` | `quantity` | Validation gas used by the consensus block |
| `compressed_bytes` | `quantity` | Compressed frontend payload size |
| `uncompressed_bytes` | `quantity` | Uncompressed frontend payload size |
**ReactivitySubscription** (returned by [`somnia_reactivityGetSubscriptionInfo`](#somnia_reactivitygetsubscriptioninfo) / [`somnia_reactivityGetSubscriptions`](#somnia_reactivitygetsubscriptions)):
| Field | Type | Notes |
| --------------------------- | ---------- | ------------------------------------------------ |
| `id` | `quantity` | Subscription ID |
| `topics` | `data[4]` | Log topics to match (up to 4) |
| `origin` | `address` | Origin filter |
| `caller` | `address` | Caller filter (reserved, currently unused) |
| `emitter` | `address` | Contract address that emits the matched log |
| `owner` | `address` | Subscription owner address |
| `handler_contract_address` | `address` | Contract called when the subscription triggers |
| `handler_function_selector` | `data` | 4-byte function selector on the handler contract |
| `gas_limit` | `quantity` | Gas limit for the reactive callback |
| `priority_fee_per_gas` | `quantity` | Priority fee per gas for the callback |
| `max_fee_per_gas` | `quantity` | Max fee per gas for the callback |
### Subscription Schemas
All [`eth_subscribe`](#eth_subscribe) notifications use this envelope:
```json
{"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x1","result":...}}
```
**`newHeads`**
* **Params:** none
* **Event result:** [`NewHead`](#newhead)
**`logs`**
**Params:**
| Field | Type | Notes |
| --------- | ------------------------ | -------------------------------------------------------------------------------------------------- |
| `address` | `address` or `address[]` | Match logs from one or more contracts |
| `topics` | `array` or `null` | Same topic-position semantics as [`eth_getLogs`](#eth_getlogs) / [`eth_newFilter`](#eth_newfilter) |
* **Event result:** [`Log`](#log)
**`somnia_finishedTransactions`**
**Params:**
| Field | Type | Notes |
| ------------------------------- | --------------------- | ---------------------------------------------------- |
| `transaction_senders` | `address[]` or `null` | Only emit events for transactions from these senders |
| `subscribe_to_all_transactions` | `bool` or `null` | Emit events for all finished transactions |
**Event result:**
| Field | Type | Notes |
| -------------------- | -------------- | ------------------------------------ |
| `transaction_id` | `hash` | Transaction hash |
| `transaction_status` | `integer enum` | Internal `TransactionStatus` value |
| `sender_address` | `address` | Transaction sender |
| `transaction_nonce` | `integer` | Sender nonce used by the transaction |
**`somnia_finishedBlocks`**
* **Params:** none
**Event result:**
| Field | Type | Notes |
| ------------------- | ---------- | ----------------------------------- |
| `blockNumber` | `quantity` | Finished block number |
| `totalTransactions` | `quantity` | Number of transactions in the block |
**`somnia_watch`**
**Params:**
| Field | Type | Notes |
| ------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `address` | `address` or `address[]` | Log emitter filter |
| `topics` | `array` or `null` | Same topic-position semantics as [`eth_getLogs`](#eth_getlogs) / [`eth_newFilter`](#eth_newfilter) |
| `eth_calls` | `array` | Each item is either a [`TransactionRequest`](#transactionrequest), or \[[`TransactionRequest`](#transactionrequest), [`StateOverrideSet`](#stateoverrideset)] |
| `context` | `string` or `string[]` | Extra values appended to each simulated call's calldata. Supported selectors: `address`, `data`, `topic1`, `topic2`, `topic3`, `topic4` |
| `push_changes_only` | `bool` or `null` | Only emit when `simulationResults` change from the previous event for that subscription |
**Event result:**
| Field | Type | Notes |
| ------------------- | --------- | ----------------------------------------------- |
| `address` | `address` | Log emitter |
| `topics` | `hash[]` | Emitted topics |
| `data` | `bytes` | Raw log data |
| `simulationResults` | `bytes[]` | Return data from each configured simulated call |
**NewHead** (event payload for `eth_subscribe("newHeads")`):
| Field | Type | Notes |
| ------------------ | ---------- | ---------------------------- |
| `hash` | `hash` | |
| `parentHash` | `hash` | |
| `sha3Uncles` | `hash` | Always the empty uncles hash |
| `miner` | `address` | Always `0x0000...0000` |
| `stateRoot` | `hash` | |
| `transactionsRoot` | `hash` | |
| `receiptsRoot` | `hash` | |
| `logsBloom` | `bytes` | 256-byte bloom filter |
| `difficulty` | `quantity` | Always `0x0` |
| `number` | `quantity` | Block number |
| `gasLimit` | `quantity` | |
| `gasUsed` | `quantity` | |
| `baseFeePerGas` | `quantity` | |
| `timestamp` | `quantity` | Unix timestamp |
| `extraData` | `bytes` | |
# Somnia Reactivity
Reactivity lets applications respond to on-chain events without running a polling loop. It's one of Somnia's standout features.
Somnia provides reactivity in two complementary forms:
* [**On-chain reactivity**](/developer/reactivity/reactivity-onchain) stores persistent event subscriptions in chain state and then executes contract handlers when matching events are committed. The reaction is part of chain execution.
* [**Off-chain reactivity**](/developer/reactivity/reactivity-offchain) is a WebSocket feature served by the node API. A client subscribes once, receives pushed events, and can ask the node to run read-only simulations for each event so that derived state arrives in the same message — minimising latency. The reaction stays in the client.
## Which one to use
Use on-chain reactivity when the reaction should be part of chain execution, i.e. a guaranteed, public effect. For example: automatic token rebalancing, scheduled settlement, or a callback that must fire even if the original owner is offline.
Use off-chain reactivity when the reaction should stay in your app. For example: a UI that updates when an event lands, an indexer that writes to a database, or a bot that watches a pool. Off-chain subscriptions are more flexible and don't require spending SOMI.
The two kinds of reactivity are independent. You can use both at once.
# On-chain Reactivity
On-chain reactivity lets a smart contract instantly react to events in the same block, without anyone sending an event handling transaction. A user creates a subscription through a call to a precompile, and the subscription persists in chain state. When an event log or system event matches the subscription's filter, validators include a synthetic transaction in the block that calls the subscription's handler contract. The creator of the subscription pays the gas.
This is a powerful feature. As far as we know, no other EVM can do this, and when combined with Somnia's 100 ms blocks, on-chain reactivity enables a class of applications that other chains can only approximate with off-chain infrastructure.
For client-side reactions to events, see [off-chain reactivity](/developer/reactivity/reactivity-offchain).
## Example use cases
* Trigger a callback every time a specific ERC-20 Transfer event is emitted, regardless of who sent the transaction.
* Run scheduled upkeep at every epoch boundary.
* Move funds out of escrow at a predetermined time.
* Build an automated liquidation bot as a contract rather than an off-chain service.
* Forward DEX events to a settlement contract without the trader paying the forwarding cost.
## Quick start
1. Install the Solidity reactivity package (see [Solidity package](#solidity-package) below).
```bash
npm install @somnia-chain/reactivity-contracts
```
2. Write a handler contract. Inherit from `SomniaEventHandler` and override the protected `_onEvent` hook:
```solidity
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 {
// react to the event here
}
}
```
3. Fill in a `SomniaExtensions.SubscriptionFilter` and a `SomniaExtensions.SubscriptionOptions`. See [Solidity package](#solidity-package) for the struct shapes, and [Filter semantics](#filter-semantics) for what the filter fields mean.
4. Deploy the contract, and ensure the contract account holds at least 32 [SOMI](/developer/network-info/somi-coin) (see [Minimum balance](#minimum-balance)).
5. Create the subscription by calling `SomniaExtensions.subscribe(address(this), filter, options)`. The call returns the subscription ID, and the calling contract becomes the subscription owner. For one-shot subscriptions at a specific block, epoch, or timestamp, use `scheduleSubscriptionAtBlock` / `scheduleSubscriptionAtEpoch` / `scheduleSubscriptionAtTimestamp` instead.
6. The subscription runs until you remove it or the owner account runs out of SOMI (see [Automatic removal](#automatic-removal)).
7. Query subscriptions off-chain via the [`somnia_reactivityGetSubscriptionInfo`](#rpc-methods) or [`somnia_reactivityGetSubscriptions`](#rpc-methods) JSON-RPC methods, or on-chain via `SomniaExtensions.getSubscriptionInfo(id)`.
8. Remove the subscription by calling `SomniaExtensions.unsubscribe(id)` from the owning contract.
These steps assume the contract itself is the subscription owner, which is what the `SomniaExtensions` library is designed for. To own the subscription directly from an EOA, see the [TypeScript package](#typescript-package), or for the raw ABI-level surface, see [Precompile](#precompile) and [`SubscriptionData`](#subscriptiondata) in the Reference section.
## How it works
Subscriptions are created by calling the **Somnia reactivity precompile** at address `0x0100`.
When a transaction is executed, every event log it emits is checked against all active subscriptions. Matching subscriptions are placed on a per-block priority queue, ordered by each subscription's `priorityFeePerGas`.
After the block's user transactions have executed, the node drains the queue and executes each pending handler as a synthetic transaction.
These reactive transactions appear in the block's reactivity output alongside the ordinary transactions. They are visible on the block explorer, have normal receipts, emit normal logs, and pay normal gas.
Each reactive transaction has:
* `from` equal to the subscription owner
* `to` equal to the subscription's handler contract
* `msg.sender` inside the call equal to `0x0100`
* Calldata equal to `handlerFunctionSelector` followed by the ABI-encoded tuple `(address emitter, bytes32[] eventTopics, bytes data)` from the matching log
* A block-unique nonce derived from the block number and position in the reactivity queue (so that reactive transactions don't conflict with the owner's regular nonces)
If the handler reverts, runs out of gas, or the owner can't pay, the reactive transaction fails in the ordinary way. A failure doesn't itself remove the subscription, but see [Automatic removal](#automatic-removal) below.
Logs emitted by reactive transactions are checked against subscriptions the same way as user transactions, and any matches are immediately added to the block's reactivity queue. Note that this means that *a subscription can provoke a recursive explosion*, unstoppably draining the owner's balance.
## Reference
### Precompile
* Address: `0x0100`
* Solidity constant: `SomniaExtensions.SOMNIA_REACTIVITY_PRECOMPILE_ADDRESS`
The precompile exposes three functions. The function selectors are keccak256 of the signatures below.
| Function | Signature |
| --------------------- | ----------------------------------------------------------------------------------------------- |
| `subscribe` | `subscribe((bytes32[4],address,address,address,address,bytes4,uint64,uint64,uint64,bool,bool))` |
| `unsubscribe` | `unsubscribe(uint256)` |
| `getSubscriptionInfo` | `getSubscriptionInfo(uint256)` |
Note that `subscribe` takes a single tuple parameter (the `SubscriptionData` struct), not eleven positional parameters — the outer parentheses are significant.
### `SubscriptionData`
The tuple passed to `subscribe`, in order:
| Field | Type | Purpose |
| ------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `eventTopics` | `bytes32[4]` | Topic filters. A zero value means "match any". (See [Filter semantics](#filter-semantics).) |
| `origin` | `address` | Matches logs from transactions sent by this address. Zero means "match any". |
| `caller` | `address` | Reserved. Pass `0x0`. |
| `emitter` | `address` | Matches logs emitted from this contract address. Zero means "match any". Set to `0x0100` to subscribe to system events (see [System events](#system-events)). |
| `handlerContractAddress` | `address` | The contract called when a match occurs. Must be non-zero. |
| `handlerFunctionSelector` | `bytes4` | The 4-byte function selector invoked on the handler contract. |
| `priorityFeePerGas` | `uint64` | Tip to validators, in wei. Determines order in the reactivity queue (see [Limits](#limits)). |
| `maxFeePerGas` | `uint64` | Maximum total fee per gas, in wei. If set to zero, the protocol chooses a maximum. |
| `gasLimit` | `uint64` | Maximum gas the handler may consume per invo프로젝트로 돌아가기