Skip to content

Macro Reference

Hyperstack uses Rust procedural macros to define data pipelines declaratively. These macros transform your Rust structs into a full streaming pipeline specification (AST), which is then used for both local execution and cloud deployment.


The entry point for any Hyperstack stream definition. It must be applied to a pub mod that contains your entity definitions.

#[hyperstack(idl = "idl.json")]
pub mod my_stream {
// Entity definitions...
}

Arguments:

ArgumentTypeRequiredDescription
idlstringNo*Path to an Anchor IDL JSON file relative to Cargo.toml.
protostring | arrayNo*Path(s) to .proto files for Protobuf-based streams.
skip_decodersboolNoIf true, skips generating instruction decoders (useful for manual decoding).

* Either idl or proto must be provided.


Defines a struct as a Hyperstack entity (state projection). Each entity results in a separate typed stream.

#[entity(name = "TradeTracker")]
struct Tracker {
// Field mappings...
}

Arguments:

ArgumentTypeRequiredDescription
namestringNoCustom name for the entity. Defaults to the struct name.

These macros are applied to fields within an #[entity] struct to define how data is captured and updated.

Maps a field from a Solana account directly to an entity field.

#[map(BondingCurve::virtual_sol_reserves, strategy = LastWrite)]
pub reserves: u64,

Arguments:

ArgumentTypeRequiredDescription
frompathYesSource account field (e.g., AccountType::field_name).
primary_keyboolNoMarks this field as the primary key for the entity.
lookup_indexboolNoCreates a searchable index for this field.
strategyStrategyNoUpdate strategy (default: SetOnce).
transformTransformNoTransformation to apply before storing.
renamestringNoCustom target field name in the projection.
temporal_fieldstringNoSecondary field for temporal indexing.
join_onstringNoField to join on for multi-entity lookups.

Maps a field from an instruction’s arguments or accounts.

#[from_instruction(PlaceTrade::amount, strategy = Append)]
pub trade_amounts: Vec<u64>,

Arguments: Accepts the same arguments as #[map].

Captures multiple fields from an instruction as a single structured event.

#[event(
from = PlaceTrade,
fields = [amount, accounts::user],
strategy = Append
)]
pub trades: Vec<TradeEvent>,

Arguments:

ArgumentTypeRequiredDescription
frompathYesThe source instruction type.
fieldsarrayYesList of fields to capture. Use accounts::name for instruction accounts and args::name (or data::name) for arguments.
strategyStrategyNoUpdate strategy (default: SetOnce).
transformsarrayNoList of (field, Transform) tuples for processing captured fields.
lookup_byfieldNoField used to resolve the entity key.
renamestringNoCustom target field name.
join_onfieldNoJoin field for multi-entity lookups.

Captures the entire state of a source account as a snapshot.

#[snapshot(from = BondingCurve, strategy = LastWrite)]
pub latest_state: BondingCurve,

Arguments:

ArgumentTypeRequiredDescription
frompathNoSource account type (inferred from field type if omitted).
strategyStrategyNoOnly SetOnce or LastWrite allowed.
transformsarrayNoList of (field, Transform) tuples for specific sub-fields.
lookup_byfieldNoField used to resolve the entity key.
renamestringNoCustom target field name.
join_onfieldNoJoin field for multi-entity lookups.

Defines a declarative aggregation from instructions.

#[aggregate(from = [Buy, Sell], field = amount, strategy = Sum)]
pub total_volume: u64,

Arguments:

ArgumentTypeRequiredDescription
frompath | arrayYesInstruction(s) to aggregate from.
fieldfieldNoField to aggregate. Use accounts::name or args::name. If omitted, performs Count.
strategyStrategyNoSum, Count, Min, Max, UniqueCount.
conditionstringNoBoolean expression (e.g., "amount > 1_000_000").
transformTransformNoTransform to apply before aggregating.
lookup_byfieldNoField used to resolve the entity key.
renamestringNoCustom target field name.
join_onfieldNoJoin field for multi-entity lookups.

Defines a field derived from other fields in the same entity using a Rust-like expression.

#[computed(total_buy_volume + total_sell_volume)]
pub total_volume: u64,

Arguments: Takes a single Rust expression. Can reference other fields in the entity.

Derives values from instruction metadata or arguments.

#[derive_from(from = [Buy, Sell], field = __timestamp)]
pub last_updated: i64,

Arguments:

ArgumentTypeRequiredDescription
frompath | arrayYesInstruction(s) to derive from.
fieldfieldYesTarget field. Can be a special field or a regular instruction arg.
strategyStrategyNoLastWrite or SetOnce.
conditionstringNoBoolean expression for conditional derivation.
transformTransformNoTransform to apply.
lookup_byfieldNoField used to resolve the entity key.

Special Fields:

FieldDescription
__timestampThe Unix timestamp of the block containing the instruction.
__slotThe slot number of the block.
__signatureThe transaction signature (Base58 encoded).

Declarative hooks are used at the struct level (usually on empty marker structs) to define complex key resolution logic and PDA mappings.

Defines how an account’s primary key is resolved. This is essential when an account doesn’t store its “owner” ID directly, but its address can be derived via PDA or looked up in a registry.

#[resolve_key(
account = UserProfile,
strategy = "pda_reverse_lookup",
lookup_name = "user_pda"
)]
struct UserResolver;

Arguments:

ArgumentTypeRequiredDescription
accountpathYesThe account type this resolver applies to.
strategystringNo"pda_reverse_lookup" (default) or "direct_field".
lookup_namestringNoThe name of the registry to use for reverse lookups.
queue_untilarrayNoList of instructions to wait for before resolving (ensures PDA is registered).

Registers a mapping between a PDA address and a primary key during an instruction. This mapping is stored in a temporary registry to enable #[resolve_key] to work for accounts that are created or updated in the same transaction.

#[register_pda(
instruction = CreateUser,
pda_field = accounts::user_pda,
primary_key = args::user_id,
lookup_name = "user_pda"
)]
struct PdaMapper;

Arguments:

ArgumentTypeRequiredDescription
instructionpathYesThe instruction where the PDA is created/referenced.
pda_fieldfieldYesThe field containing the PDA address (e.g., accounts::user_pda).
primary_keyfieldYesThe primary key to associate with this PDA (e.g., args::user_id).
lookup_namestringNoThe name of the registry to store this mapping in.

Registers a PDA mapping during an instruction to enable reverse lookups later.

#[register_pda(
instruction = CreateUser,
pda_field = accounts::user_pda,
primary_key = args::user_id
)]
struct PdaMapper;

StrategyDescription
SetOnceOnly write if the field is currently empty.
LastWriteAlways overwrite with the latest value.
AppendAppend to a Vec.
MergeDeep-merge objects (for nested structs).
MaxKeep the maximum value.
SumAccumulate numeric values.
CountIncrement by 1 for each occurrence.
MinKeep the minimum value.
UniqueCountTrack unique values and store the count.
TransformDescription
Base58EncodeEncode bytes to Base58 string (default for Pubkeys).
Base58DecodeDecode Base58 string to bytes.
HexEncodeEncode bytes to Hex string.
HexDecodeDecode Hex string to bytes.
ToStringConvert value to string.
ToNumberConvert value to number.