get_action_traces
discontinued
Retrieve a stream of executed actions, filtered by receiver
and account
Important
We strongly suggest to use our GraphQL search API instead of this stream. Advantages of using the GraphQL version:
- Fork-aware, able to tell you if an action was rollout due to being part of a forked block.
- Possibility to also perform a paginated query instead of streaming.
- Possibility to greatly reduce bandwidth transfer & cost (ingress to your server) by specifying the exact trimmed down data payload you need (excellent for browser & mobile usage).
- A much cleaner interface to query by block range (
lowBlockNum
andhighBlockNum
instead of harder to reason aboutstartBlock
andblockCount
) - On-the-fly ABI decode to JSON smart contract database rows that changed due to the execution of the transaction.
See the Conversion to GraphQL API section for steps on how to perform the conversion.
Usage
Note
Theget_action_traces
call is streaming only actions that are part of an executed transaction. That means
you will never receive an action matching your filter input that is part of a soft or hard fail transaction.
The API used to return all actions regardless of the transaction status, but it’s not the case anymore.
Example request:
{
"type": "get_action_traces",
"listen": true,
"req_id": "your-request-id",
"irreversible_only": true,
"data": {
"accounts": "eosio.token",
"action_name": "transfer",
"with_inline_traces": true,
"with_dbops": true,
"with_dtrxops": true,
"with_ramops": true
}
}
Information about receiver, account and action_name
Actions on the EOS blockchain are identified by a triplet receiver
/account
/action_name
- The code on the
receiver
is called with the methodaccount
/action_name
- An action is considered a “notification” when the
receiver
is different from theaccount
field. That receiver may or may not contain instructions to run for thataccount
/action_name
pair (more details ).
Arguments
|
separated list of accounts
to match.
|
separated list of actions
to match.
accounts
. Pipe |
separated list of receivers
to match.
Stream table operations produced by each action (see TableOp).
Note
Do not confuse, the latter being describing a row changes (i.e. for example, an account’s balance) while the former describe the actual creation/deletion of a contract’s table (i.e. the encompassing structure containing the actual rows).Responses
fetch: true
is not supported forget_action_traces
listen: true
requests will stream ActionTrace objects.irreversible_only: true
ensure that you only get actions from irreversible blocks. If you call it withstart_block: (current head block)
, you will have to wait until that block becomes irreversible before you see any data streaming back.
Conversion to GraphQL API
Migrating to our GraphQL Search API is really easy and offers advantages that you really want to leverage to greatly improve your users experience. Converting to GraphQL Search API is simply a matter of crafting an equivalent search query and convert a fraction of your code to use the new format.
Assuming the following stream request message you would have used previously
{
"type": "get_action_traces",
"listen": true,
"req_id": "your-request-id",
"data": {
"accounts": "eosio.token|tethertether",
"action_names": "transfer|issue",
"with_dbops": true,
"with_dtrxops": true,
"with_ramops": true
}
}
That would result in the following GraphQL document:
subscription ($cursor: String) {
searchTransactionsForward(
query: "(account:eosio.token OR account:thetertheter) (action:transfer OR action:issue)",
cursor: $cursor
) {
undo cursor
block { num id }
trace {
id
matchingActions {
seq
receiver account name
json
dbOps { operation oldJSON { object error } newJSON { object error } }
dtrxOps { operation payer transaction { actions { account name json } } }
ramOps { operation delta usage }
}
}
}
}
Note
Eager to try out the document above? Head down straight to our GraphiQL Online Editor and press the play button in the top bar of the page.The "accounts": "eosio.token|tethertether"
argument in get_action_traces
becomes the
(account:eosio.token OR account:thetertheter)
clause while the "action_names": "transfer|issue"
argument becomes the (action:transfer OR action:issue)
clause, both of them separated by
a space which acts as a logical AND
.
With this document in hand, if you are using our JavaScript client library, updating is simple a matter of changing a single line:
// Instead of
const stream = await client.streamActionTraces(..., (message) => { ... })
// Use
const stream = await client.graphql(document, (message) => { ... })
The logic changes a bit between the two calls also. While the streamActionTraces
call generates
one message per action, the GraphQL version generates one message per matching
transaction, a transaction containing actions and you can easily find those the matched the
query using the matchingActions
field.
To use the same logic as before in GraphQL, you will need, for each message received, to loop
through matchingActions
(pseudo-code
example below, logic applies to
all languages):
for action in message.searchTransactionsForward.trace.matchingActions:
// Do your old `get_action_traces` on message logic here
The message format you will receive has also change, but making the necessary adjustments is trivial. This is especially true since in GraphQL, you have the power to pick and choose the exact field you want to receive drastically shaving bandwidth cost in most usual cases.
We will not provide a 1 to 1 mapping list as it would be too much. You can use the GraphiQL Online Editor , link and start from there, then slowly add (or remove) the fields you interested in.
Note
The link above as alimit: 1
parameter so the stream stops right after a match so it’s easier to inspect the
end result. Don’t forget to remove it to get all results back! You can also play with lowBlockNum
value to
find a matching instance (since the stream starts at HEAD block by default).
Finally, if you were using some of the more advanced WebSocket fields, here is how to convert them.
Field "irreversible_only": true
Add irreversibleOnly: true
parameter below cursor
parameter in GraphQL document:
subscription ($cursor: String) {
searchTransactionsForward(
...
cursor: $cursor,
irreversibleOnly: true
) { ... }
}
Field "with_progress": 15
Add liveMarkerInterval: 15
parameter below cursor
parameter in GraphQL document:
subscription ($cursor: String) {
searchTransactionsForward(
...
cursor: $cursor,
liveMarkerInterval: 15
) { ... }
}
Field "start_block": -350
Add lowBlockNum: -350
(or a direct block num) parameter below cursor
parameter in GraphQL document:
subscription ($cursor: String) {
searchTransactionsForward(
...
cursor: $cursor,
lowBlockNum: -350
) { ... }
}
Note
You were previously using start_block
when reconnecting to start back where you left off? GraphQL is
now using a Cursor concept to perform that operation
in a much more granular and safer manner.
When receiving messages, record the last seen message.searchTransactionsForward.cursor
value. When
re-connecting, simply pass the last seen cursor
value in the variables set sent to the GraphQL
stream. This will ensure we start back at the exact location where you left off.
Using the our JavaScript
client library? Even more simpler, simply use the stream.mark(...)
call and the library handles the rest: reconnection, cursor variables update, cursor tracking (long
term storage persistence to survive across process restarts is left to you however):
const stream = await client.graphql(document, (message) => {
if (message.type === "data") {
// Procesing here
stream.mark({ cursor: message.data.searchTransactionsForward.cursor })
}
})
Next Steps
You can use the following links to complete your code conversion to GraphQL:
- If using the JavaScript client library, checkout the Quick Start: JavaScript - Stream your first results section.
- For other languages, refers to Quick Start: Other Languages to learn how to make a GraphQL stream using your language of choice.