Streaming Actions¶
The Hyperion Stream Client allows you to subscribe to a real-time or historical stream of action traces from the blockchain. This is useful for tracking specific smart contract interactions, monitoring account activity, or building applications that react to on-chain events.
You initiate an action stream using the client.streamActions(request)
method.
client.streamActions(request)
¶
This asynchronous method sends a request to the Hyperion server to start streaming action traces based on the criteria
defined in the request
object. It returns a Promise
that resolves to a HyperionStream
instance, which you can then
use to listen for messages.
import { HyperionStreamClient, StreamActionsRequest, IncomingData, ActionContent } from "@eosrio/hyperion-stream-client";
// Assuming 'client' is an initialized and connected HyperionStreamClient instance
try {
const actionStream = await client.streamActions({
contract: "eosio.token",
action: "transfer",
account: "", // Optional: filter by notified account
start_from: 0, // Start from HEAD block
read_until: 0, // Stream indefinitely
filters: [
{ field: "act.data.to", value: "eosio.ram" }
]
});
console.log("Action stream request successful. UUID:", actionStream.reqUUID);
actionStream.on("message", (message: IncomingData<ActionContent>) => {
console.log("Received action:", message.content.act.data);
});
actionStream.on("error", (error) => {
console.error("Stream error:", error);
});
} catch (error) {
console.error("Failed to initiate action stream:", error);
}
Request Parameters (StreamActionsRequest
)¶
The request
object passed to client.streamActions()
can contain the following properties:
contract
¶
- Type:
string
- Required: Yes
- Description: The account name of the smart contract whose actions you want to stream.
- Example:
"eosio.token"
,"alien.worlds"
action
¶
- Type:
string
- Required: Yes
- Description: The name of the action you want to stream.
- You can specify a single action name (e.g.,
"transfer"
). - Use an asterisk (
"*"
) to stream all actions executed by the specifiedcontract
.
- You can specify a single action name (e.g.,
- Example:
"transfer"
,"newaccount"
,"*"
account
¶
- Type:
string
- Optional: Yes
- Default:
""
(empty string, meaning no account-specific filtering beyondcontract
andaction
) - Description: If provided, the stream will only include actions where this account is involved. Involvement can
mean:
- The account is listed in the
act.authorization
array (i.e., authorized the action). - The account is the recipient of a notification triggered by the action (i.e., listed in the
notified
array of the action trace).
- The account is listed in the
- Example:
"myuseraccnt1"
start_from
¶
- Type:
number | string
- Optional: Yes
- Default:
0
- Description: Defines the starting point of the stream.
0
: Start from the current head block of the blockchain (live streaming).- Positive Number: Start from this specific absolute block number (e.g.,
150000000
). - Negative Number: Start from a block relative to the current head block. For example,
-100
starts streaming 100 blocks before the current head. - ISO 8601 Timestamp String: Start from the block closest to the specified date and time (e.g.,
"2023-01-01T00:00:00.000Z"
).
- See Also: Block Range Parameters for a more detailed explanation.
read_until
¶
- Type:
number | string
- Optional: Yes
- Default:
0
- Description: Defines the ending point of the stream.
0
: Stream indefinitely (or untilignore_live: true
and historical data is complete).- Positive Number: Stop at this specific absolute block number.
- Negative Number: Stop at a block relative to the current head block.
- ISO 8601 Timestamp String: Stop at the block closest to this specified date and time.
- See Also: Block Range Parameters
filters
¶
- Type:
RequestFilter[]
(Array ofRequestFilter
objects) - Optional: Yes
- Default:
[]
(empty array, no additional data filters) - Description: An array of filter objects to perform server-side filtering based on the content of the action data.
Each
RequestFilter
object has:field
(string): A dot-notation path to the field within the action's data structure (e.g.,"act.data.to"
,"@transfer.from"
,"act.authorization.actor"
). Hyperion often provides special@
prefixed fields for commonly accessed, decoded data (like@transfer.from
,@transfer.to
,@transfer.quantity
).value
(string | number | boolean): The value to compare against.operator
(string, optional): The comparison operator. Defaults to"eq"
(equals). Supported operators include:"eq"
: equals"ne"
: not equals"gt"
: greater than"lt"
: less than"gte"
: greater than or equal to"lte"
: less than or equal to"contains"
: (for string fields) field contains the value"starts_with"
: (for string fields) field starts with the value"ends_with"
: (for string fields) field ends with the value
- Example:
filters: [ { field: "act.data.to", value: "teamgreymass" }, // transfer.to === 'teamgreymass' { field: "act.data.amount", value: 10000, operator: "gte" } // data.amount >= 10000 (assuming amount is numeric) { field: "@transfer.memo", value: "payment", operator: "contains" } // memo contains "payment" ]
- Note: Refer to Hyperion's index template definitions to understand available indexed fields for actions.
filter_op
¶
- Type:
'and' | 'or'
- Optional: Yes
- Default:
"and"
- Description: Specifies the logical operator to use when multiple
filters
are provided."and"
: All filter conditions must be met."or"
: At least one filter condition must be met.
- Example:
// Actions where act.data.to IS 'userA' OR act.data.from IS 'userA' filters: [ { field: "act.data.to", value: "userA" }, { field: "act.data.from", value: "userA" } ], filter_op: "or"
ignore_live
¶
- Type:
boolean
- Optional: Yes
- Default:
false
- Description:
- If
true
, the stream will stop after all historical data (up toread_until
or the current head block ifread_until
is 0) has been sent. It will not send live blocks. - If
false
, the stream will transition to sending live blocks after historical data is complete.
- If
- Example: To get only the last 100 blocks of
eosio.token::transfer
actions and then stop:{ contract: "eosio.token", action: "transfer", start_from: -100, read_until: 0, // Read up to current head ignore_live: true }
replayOnReconnect
¶
- Type:
boolean
- Optional: Yes
- Default:
false
- Description:
- If
true
, and the client disconnects and then reconnects, it will attempt to resend this stream request, automatically adjustingstart_from
to the block number after the last successfully received block for this stream. This helps prevent data loss during transient network issues. - If
false
, the stream request will not automatically replay data from the offline time on reconnect. You would need to manually set up a new stream. Current live data will keep streaming after reconnection, but you will miss the data from the offline period.
- If
- Recommendation: Set to
true
if you need the full missed sequence of data even after reconnections.
Handling the Action Stream¶
Once await client.streamActions(request)
resolves, it returns a HyperionStream
object. You primarily interact with
this object by listening to its events:
stream.on("message", (data: IncomingData<ActionContent>) => { ... })
: This is the event fired for each action trace received from the stream. Thedata
object contains:data.uuid
: The unique identifier for this stream request.data.type
: Will be"action"
.data.mode
:"live"
or"history"
.data.content
: AnActionContent
object containing the detailed action trace (e.g.,act.data
,block_num
,trx_id
).data.irreversible
:true
if this message is confirmed irreversible (relevant ifclient.options.libStream
is true). See Handling Stream Data for more onActionContent
.
For cleaner sequential processing of the data check the AsyncIterator Pattern
-
stream.on("error", (error: any) => { ... })
: Fired if an error occurs that is specific to this stream (e.g., the server terminates the stream due to an issue). -
stream.on("start", (response: { status: string, reqUUID: string, startingBlock: number | string }) => { ... })
: Fired when the Hyperion server acknowledges and successfully starts the stream request.response.reqUUID
will matchstream.reqUUID
.startingBlock
reflects the effectivestart_from
resolved by the server.
Stopping an Action Stream¶
To stop receiving data for a specific action stream and notify the server to close it:
// Assuming 'actionStream' is the object returned by client.streamActions()
actionStream.stop();
console.log("Requested to stop action stream:", actionStream.reqUUID);
Examples¶
1. Stream Live Transfers to a Specific Account¶
const stream = await client.streamActions({
contract: "eosio.token",
action: "transfer",
start_from: 0, // Live
filters: [
{ field: "act.data.to", value: USER_ACCOUNT_NAME }
],
replayOnReconnect: true
});
stream.on("message", (msg) => {
console.log(`Live transfer to ${USER_ACCOUNT_NAME}:`, msg.content.act.data);
});
2. Get All Actions by a Contract from a Specific Block Range¶
const stream = await client.streamActions({
contract: CONTRACT_NAME,
action: "*", // All actions
start_from: START_BLOCK_NUM,
read_until: END_BLOCK_NUM,
ignore_live: true // Stop after this block
});
// Using AsyncIterator for this example
for await (const message of stream) {
if (message === null) break; // Stream ended
console.log(` ${message.content.act.name}:`, message.content.act.data);
}
Next Steps¶
- Handling Stream Data: Dive deeper into the structure of
ActionContent
and explore the AsyncIterator pattern. - Streaming Table Deltas: Learn about monitoring contract table changes.
- Client Configuration: Review all Client Configuration options.