-
Notifications
You must be signed in to change notification settings - Fork 35
Tutorial: Querying for Tezos alphanet data using the ConseilJS v2 API
In this walkthrough, we will ConseilJS to query for some Tezos alphanet blockchain data.
You will need to either run your own Conseil instance or ask Cryptonomic for access to their dev
instance going against the Tezos alphanet.
When querying Conseil through ConseilJS, we will provide our functions with url
and apiKey
parameters which should look something like this:
"url": "https://myconseilnode:8080.com",
"apiKey": "myapikey"
Let us first use the metadata queries to ensure we can get the data we want. To do so, we should first import the required namespace from ConseilJS to our project.
import { ConseilMetadataClient } from 'conseiljs'
Let’s start by confirming Tezos data is indeed available from the current Conseil instance:
import { ConseilMetadataClient } from 'conseiljs'
const { getPlatforms } = ConseilMetadataClient;
const url = "https://myconseilnode.com";
const apiKey = "myapikey"
async function foo(){
const platforms = await getPlatforms(url, apiKey);
return platforms;
}
The returned platforms
variable will look like:
[
{
"name": "tezos",
"displayName": "Tezos"
}
]
Great, Tezos is indeed supported!
Now let’s enhance our code to confirm which Tezos networks this Conseil instance offers data for:
import { ConseilMetadataClient } from 'conseiljs'
const { getPlatforms, getNetworks } = ConseilMetadataClient;
const url = "https://myconseilnode.com"
const apiKey = "myapikey"
async function foo(){
const platforms = await getPlatforms(url, apiKey);
const networks = await getNetworks(url, apiKey, platforms[0].name)
return networks;
}
The returned networks
variable will look like:
[
{
"name": "alphanet",
"displayName": "Alphanet",
"platform": "tezos",
"network": "alphanet"
}
]
Alphanet is also supported by this instance! Some other Conseil instances might support multiple networks.
Now let’s enhance our code to understand what kinds of data we can query from our Conseil instance.
import { ConseilMetadataClient } from 'conseiljs'
const { getPlatforms,
getNetworks,
getEntities } = ConseilMetadataClient;
const url = "https://myconseilnode.com"
const apiKey = "myapikey"
async function foo(){
const platforms = await getPlatforms(url, apiKey);
const networks = await getNetworks(url, apiKey, platforms.name)
const entities = await getEntities(url, apiKey, networks[0].platform, networks[0].network)
return entities;
}
The returned entities
variable will look like:
[
{
"name": "blocks",
"displayName": "Blocks",
"count": 137532,
"network": "alphanet"
},
{
"name": "accounts",
"displayName": "Accounts",
"count": 5501,
"network": "alphanet"
},
{
"name": "operation_groups",
"displayName": "Operation groups",
"count": 966144,
"network": "alphanet"
},
{
"name": "operations",
"displayName": "Operations",
"count": 978238,
"network": "alphanet"
},
{
"name": "fees",
"displayName": "Fees",
"count": 259,
"network": "alphanet"
}
]
So this means we can query for information about blocks, accounts, operation groups, operations, and fees.
The entities above effectively correspond to tables in the Conseil database. Let's now investigate which columns from these tables are available.
import { ConseilMetadataClient } from 'conseiljs'
const { getPlatforms,
getNetworks,
getEntities,
getAttributes } = ConseilMetadataClient;
const url = "https://myconseilnode.com"
const apiKey = "myapikey"
async function foo(){
const platforms = await getPlatforms(url, apiKey);
const networks = await getNetworks(url, apiKey, platforms[0].name)
const entities = await getEntities(url, apiKey, platforms[0].name, networks[0].network)
const attributesForOperations = await getAttributes(url, apiKey, platforms[0].name, networks[0].network, entities[3].name)
return attributesForOperations;
}
The returned attributesForOperations
variable will look like:
[
{
"name": "operation_id",
"displayName": "Operation id",
"dataType": "Int",
"cardinality": 978326,
"keyType": "UniqueKey",
"entity": "operations"
},
{
"name": "operation_group_hash",
"displayName": "Operation group hash",
"dataType": "String",
"cardinality": 966232,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "kind",
"displayName": "Kind",
"dataType": "String",
"cardinality": 8,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "level",
"displayName": "Level",
"dataType": "Int",
"cardinality": 137398,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "delegate",
"displayName": "Delegate",
"dataType": "String",
"cardinality": 66,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "slots",
"displayName": "Slots",
"dataType": "String",
"cardinality": 355113,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "nonce",
"displayName": "Nonce",
"dataType": "String",
"cardinality": 4268,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "pkh",
"displayName": "Pkh",
"dataType": "String",
"cardinality": 674,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "secret",
"displayName": "Secret",
"dataType": "String",
"cardinality": 674,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "source",
"displayName": "Source",
"dataType": "String",
"cardinality": 1166,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "fee",
"displayName": "Fee",
"dataType": "Decimal",
"cardinality": 430,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "counter",
"displayName": "Counter",
"dataType": "Decimal",
"cardinality": 10921,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "gas_limit",
"displayName": "Gas limit",
"dataType": "Decimal",
"cardinality": 384,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "storage_limit",
"displayName": "Storage limit",
"dataType": "Decimal",
"cardinality": 103,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "public_key",
"displayName": "Public key",
"dataType": "String",
"cardinality": 1097,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "amount",
"displayName": "Amount",
"dataType": "Decimal",
"cardinality": 6574,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "destination",
"displayName": "Destination",
"dataType": "String",
"cardinality": 4297,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "parameters",
"displayName": "Parameters",
"dataType": "String",
"cardinality": 518,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "manager_pubkey",
"displayName": "Manager pubkey",
"dataType": "String",
"cardinality": 202,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "balance",
"displayName": "Balance",
"dataType": "Decimal",
"cardinality": 757,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "spendable",
"displayName": "Spendable",
"dataType": "Boolean",
"cardinality": 1,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "delegatable",
"displayName": "Delegatable",
"dataType": "Boolean",
"cardinality": 1,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "script",
"displayName": "Script",
"dataType": "String",
"cardinality": 154,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "status",
"displayName": "Status",
"dataType": "String",
"cardinality": 4,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "block_hash",
"displayName": "Block hash",
"dataType": "String",
"cardinality": 137391,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "block_level",
"displayName": "Block level",
"dataType": "Int",
"cardinality": 137392,
"keyType": "NonKey",
"entity": "operations"
},
{
"name": "timestamp",
"displayName": "Timestamp",
"dataType": "DateTime",
"cardinality": 137392,
"keyType": "NonKey",
"entity": "operations"
}
]
Wow, that's a lot of attributes to explore! Observe that the returned data lists for each attribute things like its cardinality, i.e. how many unique values the attribute has, and data type.
Querying for attributes for blocks and accounts is left as an exercise for the reader!
Let's now figure out how many different kinds of operations Conseil has in its database.
import { ConseilMetadataClient } from 'conseiljs'
const { getPlatforms,
getNetworks,
getEntities,
getAttributes,
getAttributeValues } = ConseilMetadataClient;
const url = "https://myconseilnode.com"
const apiKey = "myapikey"
async function foo(){
const platforms = await getPlatforms(url, apiKey);
const networks = await getNetworks(url, apiKey, platforms[0].name);
const entities = await getEntities(url, apiKey, platforms[0].name, networks[0].network);
const attributesForOperations = await getAttributes(url, apiKey, platforms[0].name, networks[0].network, entities[3].name);
const valuesForOperationKind = await getAttributeValues(url, apiKey, platforms[0].name, networks[0].network, entities[3].name, attributesForOperations[3].name);
return valuesForOperationKind;
}
The returned valuesForOperationKind
variable will look like:
[
"seed_nonce_revelation",
"delegation",
"transaction",
"activate_account",
"origination",
"reveal",
"double_baking_evidence",
"endorsement"
]
Of course, Tezos supports more operation kinds but these are just the ones Conseil came across in alphanet at the point at which it was queried.
Now that we know how to use metadata queries to understand the available data, we can move on to querying for data.
Now that we are armed with all the metadata we need, we can actually start pulling the data we want.
Let's try to find all delegations between blocks 10,000 and 12,000 and report both the delegators and the bakers they are delegating to. To perform this, we will be using executeEntityQuery
function of ConseilDataClient
namespace, and various functions from ConseilQueryBuilder
namespace.
executeEntityQuery
takes 5 parameters;
-
serverInfo: a ConseilServerInfo object, which has 2 fields;
url
andapiKey
-
platform: The platform we will be querying; in our case it will be
tezos
-
network: Which network of the above mentioned platform to query; in our case it will be
alphanet
-
entity: Which entity we will be querying; in our case it will be
operations
-
query: The query object which we will be building using the
ConseilQueryBuilder
functions.
In this example, we will be using all 5 ConseilQueryBuilder
functions.
addFields, queryBuilder, addPredicate, addOrdering, and *setLimit
To better understand how these functions work and what sorts of parameters they receive, please refer to our API Documentation.
import { ConseilDataClient, ConseilQueryBuilder } from 'conseiljs'
const { executeEntityQuery } = ConseilDataClient;
const { blankQuery, addFields, addPredicate, addOrdering, setLimit } = ConseilQueryBuilder;
const conseilServer = {
url: "https://myconseilnode.com/tezos/alphanet",
apiKey: "myapikey"
};
const platform = "tezos";
const network = "alphanet";
const entity = "operations";
let query = blankQuery();
query = addFields(query, ['operation_group_hash','block_level','kind','source','delegate'])
query = addPredicate(query, 'kind', 'eq', ['delegation'], false);
query = addPredicate(query, 'block_level', 'between', [10000,12000],false);
query = addOrdering(query, 'balance', 'desc');
query = setLimit(query, 1000);
async function foo(){
const queryResults = await executeEntityQuery(conseilServer, platform, network, entity, query);
return queryResults;
}
The queryResults
variable will return:
[
{
"source": "tz1SsvTqeMmR9FW1D81dw9AaZPPwxSKFGuja",
"block_level": 10232,
"operation_group_hash": "onjnC72xRyE1Z2mydkGPRJkmiawFi9Y78JVKQQo4chd7oJLcFrz",
"delegate": "tz1SsvTqeMmR9FW1D81dw9AaZPPwxSKFGuja",
"kind": "delegation"
},
{
"source": "tz1db53osfzRqqgQeLtBt4kcFcQoXJwPJJ5G",
"block_level": 11143,
"operation_group_hash": "op4GYeewLZt9kvv3PYDWJph9cAZTVirrp6uJXVbxBg7JWnULg34",
"delegate": "tz1db53osfzRqqgQeLtBt4kcFcQoXJwPJJ5G",
"kind": "delegation"
},
{
"source": "KT1Bq7LEmYJyjghJAMMxt3V9FqXy9pfXAqrq",
"block_level": 11826,
"operation_group_hash": "opPs7KAVZHdieascPba2KwK5EkmHvX7ZyjGkrVWYepFLUQRg4qV",
"delegate": "tz1LdZ6S8ScNMgaCLqrekDvbBWhLqtUebk23",
"kind": "delegation"
},
{
"source": "tz1Q9oZm21A3jhHjNUtrnVYEy8PbUeBf2wUV",
"block_level": 11029,
"operation_group_hash": "oodzAkkQyYeUHN9SpLf4dnGwcehxsY9UKGTXVoekc4ogX5R9a9Q",
"delegate": "tz1Q9oZm21A3jhHjNUtrnVYEy8PbUeBf2wUV",
"kind": "delegation"
},
{
"source": "tz1bxe2zpe15B2TKajfT67Zp5oey6ptb5bsv",
"block_level": 11236,
"operation_group_hash": "onrpv2ana8A3X5T97hi2HGESd4frNfpuHuXL2mNAxwuhwZP25jH",
"delegate": "tz1bxe2zpe15B2TKajfT67Zp5oey6ptb5bsv",
"kind": "delegation"
}
]
In the above query we
- asked for five specific columns
- applied two conditions
- set a specific sort order
- limited the returned results to 1,000 records.
As it turns out, there were five delegation operations between blocks 10,000 and 12,000.
Thanks for reading this tutorial so far. If you encounter any issues, feel free to join our Riot group for developers and get help. As people read this tutorial and provide feedback, we will add more example queries so make sure to come back here to learn more. Happy querying!