Eventing & indexing
Watching for on-chain state changes is the second-most-common task after creating them. Boson exposes three eventing surfaces; pick by latency tolerance and throughput.
The three surfaces
| Surface | Latency | Throughput | Best for |
|---|---|---|---|
RPC logs (eth_getLogs, WebSocket) | 0–1 block | Limited by node | Real-time triggers, single-app dashboards |
| Subgraph (The Graph) | 1–3 blocks | Unlimited GraphQL | Marketplaces, listings, history queries |
| Your own webhook server | Same as input (RPC or subgraph) | Whatever you build | Persisting orders to your CRM / fulfillment system |
The subgraph is convenient (typed GraphQL, joins across offers/exchanges/disputes), but it lags. Plan for it.
The subgraph indexing lag
After a transaction is mined, the Boson subgraph waits for the indexer to process it — typically 1–3 blocks. If you commit at block N and immediately read getExchanges(), you may get nothing for blocks N..N+3.
Don't poll the subgraph in a tight loop. Use the SDK helper:
const tx = await sdk.commitToOffer(buyer, offerId)
const receipt = await tx.wait()
await sdk.waitForGraphNodeIndexing(receipt.blockNumber)
const exchanges = await sdk.getExchanges({ where: { buyer } })waitForGraphNodeIndexing(blockNumber) polls the subgraph's _meta until the indexed block ≥ blockNumber. Default timeout is ~30 seconds.
Inside an agent loop, treat this as mandatory. See Build for AI agents → Idempotency & retry and Troubleshooting → Subgraph indexing lag.
Reading from RPC logs
For zero-lag, listen to events directly:
import { ethers } from "ethers"
const diamond = new ethers.Contract(diamondAddress, diamondAbi, provider)
diamond.on("BuyerCommitted", (offerId, buyerId, exchangeId, exchangeStruct, /* … */) => {
// … immediately persist or react
})The Diamond emits events from every facet at the same contract address. Use the merged ABI from @bosonprotocol/common.
Critical events
| Event | Emitted by | Fires on |
|---|---|---|
OfferCreated | OfferHandlerFacet | createOffer |
OfferVoided | OfferHandlerFacet | voidOffer |
BuyerCommitted | ExchangeCommitFacet | commitToOffer (creates an exchange) |
VoucherRedeemed | ExchangeHandlerFacet | redeemVoucher |
ExchangeCompleted | ExchangeHandlerFacet | completeExchange |
VoucherCanceled | ExchangeHandlerFacet | cancelVoucher (buyer) |
VoucherRevoked | ExchangeHandlerFacet | revokeVoucher (seller) |
DisputeRaised | DisputeHandlerFacet | raiseDispute |
DisputeResolved | DisputeHandlerFacet | resolveDispute |
DisputeDecided | DisputeHandlerFacet | decideDispute |
FundsDeposited | FundsHandlerFacet | depositFunds |
FundsWithdrawn | FundsHandlerFacet | withdrawFunds |
Full list at Reference → Contracts → Events.
Building a webhook server
Run an event listener that posts to your backend. See Recipes → Webhook server for state changes for a complete reference implementation.
The common shape:
RPC subscription or subgraph poll
Pull events from a chain RPC (eth_subscribe / log filter) or by polling the subgraph.
Filter and dedupe
Match the event types you care about; remember the last block + log index so you don't fire twice if the source replays.
HTTP POST to your handler
Deliver the event to your backend, with retries on non-2xx responses.
Reorg handling
Every chain Boson runs on can reorg. Defensive practices:
- Wait 2–3 confirmations before treating an event as final on Polygon / Base / Optimism / Arbitrum; 12+ on Mainnet.
- Make your webhook handler idempotent — keyed on
(txHash, logIndex)or(exchangeId, newState). - For high-stakes actions, re-read state from RPC before acting (don't trust the event payload alone).
Common footguns
- Subgraph "missing data" right after a write. Indexing lag. Use
waitForGraphNodeIndexing. - Polygon RPC rate limits. Free-tier RPCs throttle. Use a paid provider for production.
- Event ABIs from old versions. If you read events from a Diamond before an upgrade and decode with a post-upgrade ABI, field order can shift. Pin the ABI to the version you indexed at.
Where to look in code
- Reference → Contracts → Events
- Reference → Core SDK → Marketplace mixin — subgraph queries
- Reference → Subgraph queries