Push-based event subscriptions over eth_subscribe.
The dominant pattern for reactive dapps. Where Web3.Contract.Event
fetches historical logs via eth_getLogs, this module opens a long-lived
WebSocket subscription so every NEW log is pushed to your update fn the
moment it lands on-chain — no polling, no missed blocks.
Subscriptions are tagged with an opaque SubscriptionId
so multiple subscriptions can coexist. Pass the same id to
close to stop a subscription, and match incoming
LogEvents against it to route updates.
Build a SubscriptionId from a label. Labels must be unique within a
session; reusing a label closes the previous subscription with the same
label.
factorySid =
Subscription.subscriptionId "factory:TokenCreated"Read the raw string back out of a SubscriptionId. Useful for routing
incoming events in your update fn.
Subscriptions are built with a tiny pipeline: start with logs,
narrow with atAddress, withTopic, or
withTopics:
factoryEvents : SubscriptionId -> Cmd msg
factoryEvents sid =
Subscription.logs
|> Subscription.atAddress factoryAddress
|> Subscription.withTopic 0 tokenCreatedTopicHash
|> Subscription.open sidOpaque, fluent builder for an eth_subscribe("logs", ...) filter.
Narrow the filter to logs emitted by a specific contract.
Match a specific topic at a given position. Topic 0 is always the event signature hash; topics 1–3 are the event's indexed parameters in declaration order.
-- match ERC-20 Transfer(address indexed from, address indexed to, uint256)
Subscription.logs
|> Subscription.atAddress tokenAddress
|> Subscription.withTopic 0 transferTopic
|> Subscription.withTopic 2 myAddressTopicReplace the entire topics list with a pre-built list of optional topic
hashes. Nothing at a position means "match anything" — use the variadic
withTopic for the common case.
A single log event pushed by the subscription.
blockNumber and logIndex are zero when unavailable (pending logs in
some node implementations). removed is True if a chain reorg made
this log no longer canonical — your update fn should usually treat a
removed log as an "undo" of an earlier emission.
The high-level lifecycle of a subscription. Surfaced via
statusDecoder so you can show "connecting…" /
"reconnecting…" indicators.
Opening — handshake in progress, no events yet.Open — chain push is live.Closed — socket closed; the runtime will retry automatically.Failed err — the WS endpoint refused or the chain rejected the filter;
the runtime will NOT retry without an explicit close + open cycle.The Cmd helpers below encode a port payload for elm-web3-ports.js
to consume. Pair them with the decoders to interpret incoming subscription
events.
Open the subscription. Encodes to the watchEvent Cmd that
elm-web3-ports.js
consumes.
Close an open subscription by id. The runtime stops pushing events and (if the WS connection has no other active subscriptions) closes the socket.
Decode a { tag: "subscribed", id, status } port message into a
( SubscriptionId, Status ) pair.
Decode a { tag: "eventLog", id, … } port message into a
( SubscriptionId, LogEvent ) pair.
Opaque tag identifying a subscription. Construct with
subscriptionId.