Simple Contract Event Query

Anyblock Analytics GmbH
6 min readMar 14, 2021
Featured image: Neon handshake. Source Unsplash.

Welcome!

Within this tutorial, you will learn how to retrieve and analyze data from the Ethereum Blockchain with the help of the anyblock.tools API.

We will show you how to retrieve data from anyblock.tools using the Elasticsearch Query DSL.

You will learn

  • How to access anyblock.tools
  • How to use different methods to query the anyblock.tools API
  • How to write a basic query returning some events
  • How a return object is structured and which data it returns
  • How to filter events for a specific contract or a specific event type
  • How to sort the events by block number

What you must know already

This tutorial is written for programmers, who have some experience with JSON, Rest-APIs, and the basic structure of HTTP-requests.

What you need

If you want to play around with the HTTP-requests, you should install an HTTP client. The good ol’ terminal users might use cURL. For advanced usage and a graphical UI, we recommend using Postman. We provide copy-pasteable commands for cURL throughout the tutorial, so if you want to follow along, it is advisable to install the software first.

You also will need a registered anyblock.tools API-Account to run the REST calls. The token of your account will be used in the following tutorial as $token. Please replace the variables with your user and password.

Create an anyblock.tools query step-by-step

Retrieve all events indexed by anyblock.tools

On the anyblock.tools endpoint ethereum/ethereum/mainnet/es/event/search, you can query all events from the Ethereum mainnet.

A simple GET request to /ethereum/ethereum/mainnet/es/event/search shows us 10 events in no particular order.

Execute the request with cURL:

curl -X POST \
https://api.anyblock.tools/ethereum/ethereum/mainnet/es/event/search/ \
-H' Authorization: Bearer $mytoken' \
-H' Content-Type: application/json'

The returned JSON starts with meta-information about the processing of the query (not shown here).

The query results are shown under the "hits" keyword in the retrieved JSON data-structure:

"hits":{
"total":69502921,
"max_score":1,
"hits":[
...
]
}

You get the "total" number of hits. It represents the total number of events in the anyblock.tools index.

The "max_score" isn’t very interesting to us in general, because we mostly filter for boolean conditions, that can only be 0 (not returned by the query at all) or 1.The actual events are listed under the hits.hits keyword. If we look at one of the events, we can observe the general structure of an event.

{
"_index":"ethereum_2",
"_type":"event",
"_id":"0x92c1b864051b9e6758ab217bc70e0d8641d5f830e16b0a7d15ba78ef2356ba9c_e_52",
"_score":1,
"_routing":"0x251d33d4ab03fb675bb2d09304a4aca28b943373c0bd8dbc85402d9e23f4f061",
"_parent":"0x92c1b864051b9e6758ab217bc70e0d8641d5f830e16b0a7d15ba78ef2356ba9c",
"_source":{
"args":[
{
"name":"hash",
"value.hex":"b'eb8dd23ef00be18cb4a263b4271e2f9c28bb47a239f179001691f6e887a6ed47'",
"value.num":null,
"value.scaled":null,
"value.type":"bytes32",
"pos":0
},
{
"name":"registrationDate",
"value.hex":"0x59948642",
"value.num":1502905922,
"value.type":"uint256",
"pos":1,
"value.scaled":null
}
],
"event":"AuctionStarted",
"logIndex":{
"num":52,
"raw":"0x34"
},
"transactionIndex":{
"num":92,
"raw":"0x5c"
},
"transactionHash":"0x92c1b864051b9e6758ab217bc70e0d8641d5f830e16b0a7d15ba78ef2356ba9c",
"address":"0x6090a6e47849629b7245dfa1ca21d94cd15878ef",
"blockHash":"0x251d33d4ab03fb675bb2d09304a4aca28b943373c0bd8dbc85402d9e23f4f061",
"blockNumber":{
"num":4145267,
"raw":"0x3f4073"
},
"error":null,
"str":"AuctionStarted(b\"\\xeb\\x8d\\xd2>\\xf0\\x0b\\xe1\\x8c\\xb4\\xa2c\\xb4'\\x1e/\\x9c(\\xbbG\\xa29\\xf1y\\x00\\x16\\x91\\xf6\\xe8\\x87\\xa6\\xedG\", 1502905922)",
"timestamp":"2017-08-11T17:52:02"
}
}

Again, we see meta information that is related to Elasticsearch internals (not shown here).

We want to focus on the event fields, under the "_source" keyword:

  • "event" – event name
  • "blockNumber" – the block, where it was omitted
  • "timestamp" – approximate timestamp, when it was included in the blockchain

Each argument of an event is an element in a list "args".

Filter events from a specific contract

You are probably interested in filtering for events that belong to a specific smart contract.

To demonstrate that, we will examine one of the DAI’s DSToken contracts.

The contract for the DAI Stablecoin on the mainnet resides under the address 0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359

The "address" field is where the originating contract address is given. You will have to restrict the results with Elasticsearchs filtering methods.

We don’t want to use the very limited GET query. We will send a POST request to anyblock.tools, where we provide additional parameters in the body of the HTTP-request:

{
"query":{
"bool":{
"filter":{
"term":{
"address":"0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"
}
}
}
}
}

Execute the request with cURL:

curl -X POST \
https://api.anyblock.tools/ethereum/ethereum/mainnet/es/event/search/ \
-H' Authorization: Bearer d2560f14-1935-44e7-ad3e-a1718dc03bd2' \
-H' Content-Type: application/json'
-d '{
"query":{
"bool":{
"filter":
{
"term":{
"address":"0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"
}
}
}
}
}'

The query has to be specified in the "query" parameter. We use a filter context"bool":{"filter":...} because we are only interested in filtering elements.

In the "term" parameter of the filter context, we require the results to exactly match the specified value in the "address" argument of the event, namely the address of the DAI contract.

Filter for a specific type of event

Now every event under the hits.hits keyword originates from the contract of interest. but there are still different types of events present in the queries result.

The "event" field contains the name of the event, and if you look through the results from the last query, you will most likely see 2 different types of events, Approval and Transfer.

Note: the feature of filtering by arguments and cleartext names of events is unique to anyblock.tools and its most outstanding feature. When using the usual web3 interface, an event and its values are encoded in a 64-byte hex string. To decode the event to a human-readable and easy to filter representation, the hex string has to be decoded with the help of the ABI of the events contract.

In anyblock.tools, the events are already decoded and indexed for you!

The DAI contract is following the ERC20 token standard.

From the DAI-Stablecoins ERC20 contracts code, we can see what events are defined:

contractERC20Events{ eventApproval(addressindexedsrc,addressindexedguy,uintwad); eventTransfer(addressindexedsrc,addressindexeddst,uintwad); }

If we are interested in one type of event ("Transfer"), we have to introduce another "term" filter, that gets appended to the "filter" list:

{ "query":{ "bool":{ "filter":[ { "term":{ "event.raw":"Transfer" } }, { "term":{ "address":"0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359" } } ] } } }

The "event" field defaults to a text type for full-text searching. We want to match the event name exactly (case sensitive), so we filter for the event.raw field, which is of type keyword. To learn more about the differences between text and keyword types in Elasticsearch, look here.

Execute the request with cURL:

curl -X POST \
https://api.anyblock.tools/ethereum/ethereum/mainnet/es/event/search/ \
-H 'Authorization: Bearer d2560f14-1935-44e7-ad3e-a1718dc03bd2' \
-H 'Content-Type: application/json'
-d '{
"query":{
"bool":{
"filter":[
{
"term":{
"event.raw":"Transfer"
}
},
{
"term":{
"address":"0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"
}
}
]
}
}
}'

Retrieving sorted results

You may notice that the "timestamp" of the events is outdated and that they are not sorted by their "blockNumber".

To change that, the query has to be modified again:

{ "query":{ "bool":{ "filter":[ { "term":{ "event.raw":"Transfer" } }, { "term":{ "address":"0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359" } } ] } }, "sort":{ "blockNumber.num":{ "order":"desc" } }, "size":5 }

The "sort" parameter outside of the "query" nesting tells anyblock.tools which field should be used for sorting.

We specify the .num attribute of the blockNumber, because we want the integer representation and not a hex encoding.

With "order":"desc", the events will be sorted in descending order of the block, where they were included in the blockchain.

Execute the request with cURL:

curl -X POST \
https://api.anyblock.tools/ethereum/ethereum/mainnet/es/event/search/ \
-H 'Authorization: Bearer d2560f14-1935-44e7-ad3e-a1718dc03bd2' \
-H 'Content-Type: application/json'
-d '{
"query":{
"bool":{
"filter":[
{
"term":{
"event.raw":"Transfer"
}
},
{
"term":{
"address":"0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"
}
}
]
}
},
"sort":{
"blockNumber.num":{
"order":"desc"
}
},
"size":5
}'

Restricting result size

In the last query, we specified the "size" parameter with a value of 5. This will limit the number of retrieved events to 5. For testing queries, it is advisable to set this to a small number.

With "size":-1, all filtered results are retrieved from the server. You will need to use this in conjunction with a carefully selected range filter, for example, a range of block-numbers.

Where to go from here

The best starting point is the Elasticsearch documentation. There you’ll learn how to construct more complex filter queries or how to combine filters with boolean logic.

If you are not interested in single events, but rather in cumulated properties and statistics, you should have a look at the various possibilities of aggregations.

Sascha Göbel — Co-Founder of Anyblock

Interested or questions?

Sascha Göbel
(Co-Founder)
sascha@anyblockanalytics.com
+49 6131 3272372

Follow us:
Twitter, LinkedIn,
Medium, YouTube

Originally published at https://www.anyblockanalytics.com on August 13, 2020.

--

--

Anyblock Analytics GmbH

Anyblock Analytics is a German blockchain solution provider. We offer consulting, tools and data to integrate business processes with blockchain.