@qubic.org/events
Typed event decoding and chainable filter builder for Qubic smart contract log events.
Introduction
Raw logs vs typed events
A Bob event log ("eventLogs" subscription) delivers BobLogEvent objects with a numeric eventType and a base64-encoded binary eventData. You have to know the format of each type to do anything useful with it.
decodeEvent handles that decoding:
// Before: raw
event.eventType // 0
event.eventData // "AAAAAAAAAA..." (base64)
// After: typed
const typed = decodeEvent(event)
if (typed.logType === LOG_TYPE.QU_TRANSFER) {
typed.data.source // Identity
typed.data.destination // Identity
typed.data.amount // bigint
}TypeScript narrows typed.data based on logType — no manual casting.
16 event types
@qubic.org/events covers all 16 Qubic log types plus CUSTOM_MESSAGE (255): QU transfers, asset events, contract messages, burn events, spectrum stats, oracle events, and more.
Filter builder
The eventFilter() builder is immutable and chainable — compose filters by identity, contract index, log type, and tick range, then pass the result to a subscription helper or use it manually.
Installation
bun add @qubic.org/eventsnpm install @qubic.org/eventspnpm add @qubic.org/eventsAPI reference
| Page | What's covered |
|---|---|
| Decode event | decodeEvent, LOG_TYPE constants, all typed event shapes |
| Event filter | eventFilter() builder, subscribeQuTransfers, subscribeAssetEvents, subscribeContractLogs, subscribeAllEvents |
All exports
import {
decodeEvent,
eventFilter,
subscribeQuTransfers,
subscribeAssetEvents,
subscribeContractLogs,
subscribeAllEvents,
LOG_TYPE,
EventDecodeError,
} from "@qubic.org/events"Log types at a glance
| ID | Name | Key data fields |
|---|---|---|
0 | QU_TRANSFER | source, destination, amount |
1 | ASSET_ISSUANCE | issuer, assetName, numberOfShares |
2 | ASSET_OWNERSHIP_CHANGE | issuer, assetName, owner, newOwner |
3 | ASSET_POSSESSION_CHANGE | issuer, assetName, owner, newPossessor |
4–7 | Contract messages | contractIndex, message |
8 | BURNING | sourceId, amount |
255 | CUSTOM_MESSAGE | Uint8Array |
Examples
Decode and handle events inline
import { createBobClient } from "@qubic.org/bob"
import { decodeEvent, LOG_TYPE, EventDecodeError } from "@qubic.org/events"
import { toIdentity } from "@qubic.org/types"
const bob = createBobClient({ autoResubscribe: true })
await bob.connect()
const identity = toIdentity("CFBMEMZOIDEXQAUXYYSZIURADQLAPWPMNJXQSNVQZAHYVOPYUKKJBJUCTVJL")
bob.subscribe("eventLogs", identity, (events) => {
for (const raw of events) {
try {
const typed = decodeEvent(raw)
if (typed.logType === LOG_TYPE.QU_TRANSFER) {
const { source, destination, amount } = typed.data
console.log(`Transfer: ${amount}n QU from ${source} to ${destination}`)
}
} catch (e) {
if (e instanceof EventDecodeError) console.warn("Skipping malformed event")
else throw e
}
}
})Use a subscription helper with a filter
import { createBobClient } from "@qubic.org/bob"
import { subscribeQuTransfers, eventFilter } from "@qubic.org/events"
import { toIdentity } from "@qubic.org/types"
const bob = createBobClient({ autoResubscribe: true })
await bob.connect()
const identity = toIdentity("CFBMEMZOIDEXQAUXYYSZIURADQLAPWPMNJXQSNVQZAHYVOPYUKKJBJUCTVJL")
const filter = eventFilter().fromTick(25_000_000).build()
const unsub = subscribeQuTransfers(bob, identity, (transfer, meta) => {
if (meta.isCatchUp) return
const direction = transfer.data.source === identity ? "OUT" : "IN"
console.log(`[${direction}] ${transfer.data.amount}n QU at tick ${transfer.tick}`)
}, filter)
// Later
unsub()Compose a multi-criteria filter
import { eventFilter, LOG_TYPE } from "@qubic.org/events"
import { toIdentity } from "@qubic.org/types"
const filter = eventFilter()
.forIdentity(toIdentity("CFBMEMZOIDEX..."))
.ofTypes(LOG_TYPE.QU_TRANSFER, LOG_TYPE.BURNING)
.fromTick(25_000_000)
.toTick(26_000_000)
.build()