OurGlass

Analytics

On-chain, verifiable analytics — and the attribution problem it has to solve.

OurGlass aims for a decentralized, verifiable analytics surface: charge count and token volume over time, broken down by agreement, receiver, payer, and token — all derived from public on-chain data, with no private backend required.

The attribution problem

OurGlass has no contract of its own, and it is local-first (agreements live in the payer's localStorage). A charge is a call to the shared DelegationManager plus an ERC-20 Transfer. Nothing on-chain says "OurGlass": the enforcer addresses are shared across every app on the Delegation Framework, parties vary per agreement, and the salt is unguessable. So every analytics approach reduces to one question: how do we plant an OurGlass-attributable marker on-chain that needs no central registry?

The marker: redeploy the audited enforcers

Deploy OurGlass's own instances of MetaMask's audited enforcers (unmodified bytecode) and route new agreements to them, then index those contracts' events. The emitter address of a caveat event is the enforcer instance — so attribution becomes a one-key filter (WHERE contract_address = <our enforcer>) with no central list of delegation hashes. This works precisely because the app is local-first.

This now spans both payment shapes, so OurGlass maintains two self-deployed instances:

The security implications of self-deploying audited bytecode are covered in Security.

The on-chain trace

Two sources carry everything the dashboard needs.

1. DelegationManager.RedeemedDelegation — once per redemption, for both shapes. Carries the root delegator (payer), the redeemer (receiver), and the full delegation struct including caveats and the salt (= the pinned-agreement hash).

2. The enforcer's per-charge event — filtered by our enforcer address:

  • Subscriptions: ERC20PeriodTransferEnforcer.TransferredInPeriod. The per-charge amount is the in-period delta of the cumulative transferredInCurrentPeriod, which resets each period — handle the period-boundary reset when computing it.
  • Streams: the streaming enforcer's transfer/claim event. A stream is claim-based, so the per-claim amount is read directly rather than via a period delta.

Confirm the exact streaming event name and field layout against @metamask/delegation-abis before building the indexer — do not assume it mirrors the period enforcer. Streams accrue and are claimed, so the amount semantics differ from the period enforcer's cumulative-with-reset counter.

Deriving the dashboard

From these events, filtered to our enforcer instances:

MetricDerivation
Charge / claim countcount of per-charge events
Amountperiod delta (subscriptions) or claimed amount (streams)
Volume over timesum of amounts bucketed by timestamp
Per agreementgroup by delegationHash
Per receivergroup by redeemer
Per payerjoin delegationHash → RedeemedDelegation.rootDelegator
Per tokenthe token field
Active agreementsdistinct delegationHash charged in window, cross-referenced with DisabledDelegation for revokes

Indexing

  • Shipped now — direct client-side reads. The analytics page reads the per-charge events straight from the OurGlass enforcer instances via eth_getLogs, decodes them in the browser, and derives the metrics above. No backend, no subgraph, no localStorage dependency — it works because attribution is just the emitter address.
  • Scale path — a subgraph. For larger histories, a subgraph keyed on the same events (plus RedeemedDelegation / DisabledDelegation) replaces the live scan behind the same UI. A Dune dashboard can decode the same events for sharing.

Status

The analytics page is live and reads on-chain directly. Only agreements routed to the self-deployed enforcer instances are attributable; the in-browser scan covers a bounded recent block window (ample for the current deployment), and a subgraph is the drop-in upgrade for unbounded history.

On this page