Guide 02 · Decoding and modelling
Turn transactions into reliable data
Separate decoding from business logic, preserve event order and make every derived value traceable.
- Who it is for
- Developers building protocol analytics, activity feeds and datasets from blockchain transactions.
- What you leave with
- A clear model from source record to business entity, with explicit coverage and a maintainable decoder strategy.
- Why it matters
- Readable fields are the start of an analytics model. A useful dataset also needs the right event order, entity definitions, units and rules for changes over time. This guide explains how to keep those decisions visible while reusing existing Substreams modules where they fit.
Separate source records, decoded events and business entities
Define the output before writing another parser. If the reader of your data needs a position, specify what creates, changes and closes that position. If they need a swap, specify which execution details make up one swap and which other transfers must be kept separate.
| Layer | Responsibility |
|---|---|
| Source record | Preserve the chain, block, transaction and execution context. |
| Decoded event or instruction | Interpret fields according to the relevant contract or program definition. |
| Business entity | Apply the domain rules for a position, trade, fee or balance. |
| Application metric | Define aggregation, time window, valuation and presentation. |
Keep these layers connected through stable identifiers. A dashboard discrepancy should be traceable to the underlying transaction and the transformation version. If the raw record is too large to duplicate in every row, retain a durable reference to its source.
The Substreams Solana NFT example uses exchange-specific decoding functions. It illustrates a useful boundary: sharing a chain does not mean that every protocol expresses its business activity through the same schema.
Reuse a module after checking its contract
Look for a suitable module before implementing extraction and decoding from scratch. Substreams foundational modules provide reusable transformations and filtering primitives. Their value depends on how well the particular module covers your network, protocol version and required output.
Review the actual package and source rather than relying on a familiar name. Confirm the network configuration, starting history, parameters, output schema, available documentation and licence. Record the version you choose so that a future update is a deliberate decision.
Classify the fit
- Use directly: the module already produces the data contract your application requires.
- Compose: the module decodes useful inputs, and your own module adds domain-specific interpretation.
- Extend or replace: required instructions, protocol versions or fields are missing.
A source repository and a published package are different delivery states. Check how the dependency is obtained and built. Avoid a workflow in which production depends on an undocumented local copy that another developer cannot reproduce.
Stable dependencies also make performance observations easier to interpret. If a package or dependency changes the effective module hash, previously cached execution may no longer apply. Record that change separately from changes to the business schema.
[2] Substreams foundational modules · [3] Substreams module caching
Preserve the chain-specific execution context
Choose identifiers from the source model. On Ethereum, log records expose transaction and block identities as well as log position. Preserve enough of that context to order and investigate the decoded events. For a dataset spanning several networks, include the chain identifier in the identity design.
Solana's RPC structures distinguish transaction instructions from inner instructions created through cross-program invocation. Their parent instruction index and execution context matter when reconstructing activity. A decoder that only reads the top-level instruction list can have a narrower scope than the application expects.
| Preserve | Application use |
|---|---|
| Chain and block identity | Separate networks and associate output with a particular chain history. |
| Transaction and event position | Trace records and preserve the order needed by downstream logic. |
| Contract or program identity | Choose the relevant decoder and identify its supported version. |
| Success or failure context | Apply an explicit policy to attempted versus successful activity. |
These fields are a proposed modelling checklist, not one universal schema for all chains. A multi-chain table should preserve differences that matter instead of forcing unrelated execution models into a single ambiguous event index.
[4] Ethereum JSON-RPC specification · [5] Solana RPC JSON structures
Make business rules explicit
Once a record is decoded, decide how it changes the model. A field called amount needs a unit. A balance needs an owner and a point in time. A price needs a source and a timestamp. Preserve the original quantities alongside conversions so that rounding or metadata changes remain explainable.
Specify the transformation rules
- Which events or state observations create and update each entity?
- In what order must changes be applied?
- Which fields are raw, converted, aggregated or externally enriched?
- What happens when a required value is unavailable?
- When does a previously calculated value become stale?
A useful design exercise is to describe a position from creation through several changes. Follow the actual protocol's rules, including any changes that your selected event set does not directly express. Reading the relevant contract or program is necessary before treating the model as a faithful representation of that protocol.
Keep missing values distinct from zeros. A missing price does not establish a zero-value asset, and an unknown fee does not establish a free transaction. Label incomplete calculations so that downstream consumers can decide whether to exclude or display them.
Reconcile the model with actual source activity
Build an evidence set from real transactions relevant to the model. Include ordinary activity, transactions involving several related events, failed activity where applicable and changes across protocol versions. Keep the expected interpretation and the reason for it alongside each source reference.
For each record, trace the path from execution context to decoded fields and then to the resulting entity. A parser can produce valid structured output while the business interpretation remains wrong. Review each layer separately to locate the disagreement.
| Difference observed | Investigate first |
|---|---|
| Expected activity is missing | Coverage, filters, nested execution and unsupported protocol versions. |
| Amounts disagree | Raw units, decimals, rounding and which movement the amount represents. |
| State diverges over time | Initial state, event order, omitted state changes and corrections. |
| History and live output differ | Transformation versions, finality assumptions and destination handling. |
Document what has actually been reconciled. Evidence from one protocol version or a handful of transactions does not establish complete coverage of every event type. Keep unsupported cases explicit and make them available to the people choosing whether the dataset fits their use.
Version the model and plan corrections
A decoder update can change the meaning of stored data. Treat that as a data release, not only a code deployment. Identify the affected protocols, historical interval and derived tables before deciding whether to rebuild or correct the existing output.
Keep a release record
- Dependency identity: source package, version and the configuration that produced the output.
- Schema meaning: entity definitions, field units and supported protocol versions.
- Change scope: the cases fixed or added and the history that may be affected.
- Reconciliation: real records reviewed and unresolved differences.
- Migration: how consumers move to the corrected dataset and how the previous version is retained.
Use a separate destination or versioned dataset when the new output needs comparison before adoption. Keep the consumer-facing contract stable where possible; if the meaning of a metric changes, describe that change directly.
Substreams supports a separation between reusable modules and the custom transformations that consume them. Apply that separation to ownership as well: assign responsibility for dependency updates, protocol coverage and the business model rather than assuming one module maintainer owns the entire application.
Sources and further reading
Documentation reviewed on 15 September 2026. Consult the current documentation for the release and service you use.
- Substreams: Solana NFT tradeshttps://docs.substreams.dev/how-to-guides/develop-your-own-substreams/solana/explore-solana/nft-trades
- Substreams foundational moduleshttps://docs.substreams.dev/how-to-guides/composing-substreams/foundational-modules
- Substreams module cachinghttps://docs.substreams.dev/reference-material/core-concepts/module-caching
- Ethereum JSON-RPC specificationhttps://ethereum.org/developers/docs/apis/json-rpc/
- Solana RPC JSON structureshttps://solana.com/docs/rpc/json-structures
The planning checklists are editorial recommendations. Technical product statements refer to the documentation linked in each chapter. The guides do not report measured pipeline performance.