Writing Advanced GraphQL Queries with the Unmarshal Parser

A Comprehensive Guide

Use Cases

  • You need to make configurable calls from an application
  • You want access to data to perform some computations on
  • You want quick paginated access to data stored in the parser
  • The Templates you can get from Unmarshal’s Metabase service aren’t sufficient for your application

Using the GraphQL Playground

Log in to your console account and open the GraphQL section

The GraphQL tab can be seen in your sidebar
  1. Holds documentation
  2. List of previously tried queries
  3. A refresh button to refresh your schema
  4. Opens up a list of available shortcuts
  5. Settings to change your theme
  6. The Editor
  7. The run button to test your queries
  8. Prettify and format your queries
  9. Merges multiple segments into one query
  10. Copy the query
  11. Add another editor tab
  12. The result of your query

Writing Queries

For this guide we consider a simple ERC20 contract. You can take a look at the schema and GraphiQL playground here: https://dep-jekbi9rdfyi7tuqw-graphql.prod.unmarshal.com/marshether_oklzj/graphiql

query TransferData {
allTransferEvents {
nodes {
eventFrom
eventTo
eventValue
decimalAdjustedEventValue
tokenPriceEventValue
txHash
chainId
contractAddress
}
}
}

Pagination

Of note is the length of the result array consisting of only 25 items at most. We will need to paginate to get the rest of the data. We can do this in the following two ways.

Cursor Based

Exposing pageInfo fetches us a cursors to traverse through our data

query TransferData {
allTransferEvents {
nodes {
eventFrom
eventTo
eventValue
decimalAdjustedEventValue
tokenPriceEventValue
txHash
chainId
contractAddress
}
pageInfo {
endCursor
hasNextPage
startCursor
hasPreviousPage
}
}
}
{
"data": {
"allTransferEvents": {
"nodes": [
{
"eventFrom": "0x0000000000000000000000000000000000000000",
"eventTo": "0xeba6d5b21dd4fffbd31a8719dd73eaced887b413",
"eventValue": "100000000000000000000000000",
"decimalAdjustedEventValue": "100000000",
"tokenPriceEventValue": "0",
"txHash": "0x37af853893d21a22710032994f6bf85c220a3426997fc2e885536309948a7070",
"chainId": "1",
"contractAddress": "0x5a666c7d92e5fa7edcb6390e4efd6d0cdd69cf37"
},
.
.
.
],
"pageInfo": {
"endCursor": "WyJwcmltYXJ5X2tleV9hc2MiLFsyNV1d",
"hasNextPage": true,
"startCursor": "WyJwcmltYXJ5X2tleV9hc2MiLFsxXV0=",
"hasPreviousPage": false
}
}
}
}
query TransferData {
allTransferEvents(after: "WyJwcmltYXJ5X2tleV9hc2MiLFsyNV1d") {
nodes {
eventFrom
eventTo
eventValue
decimalAdjustedEventValue
tokenPriceEventValue
txHash
chainId
contractAddress
}
pageInfo {
endCursor
hasNextPage
startCursor
hasPreviousPage
}
}
}

Offset based

You may also require an offset based pagination or maybe even a combination. Let’s say we want to fetch elements 20 to 30, we can write the following query:

query TransferData {
allTransferEvents(offset: 19, first: 11) {
nodes {
eventFrom
eventTo
eventValue
decimalAdjustedEventValue
tokenPriceEventValue
txHash
chainId
contractAddress
blockTime
id
}
}
}
{
"data": {
"allTransferEvents": {
"nodes": [
{
"eventFrom": "0x0000000000007f150bd6f54c40a34d7c3d5e9f56",
"eventTo": "0x9c5de3ad97b95a0da09fd0de84c347db450cd75c",
"eventValue": "3109172740659266715648",
"decimalAdjustedEventValue": "3109.1727406592668",
"tokenPriceEventValue": "7.243365097147954",
"txHash": "0x3f58d8cf015d685a917d9f0149d73012fcc536b653e054a4070d60ef7946c046",
"chainId": "1",
"contractAddress": "0x5a666c7d92e5fa7edcb6390e4efd6d0cdd69cf37",
"blockTime": "2021-03-30T15:58:20+00:00",
"id": "20"
},
{
"eventFrom": "0x9c5de3ad97b95a0da09fd0de84c347db450cd75c",
"eventTo": "0x8588a6c17af8984553168a9a1172ac65e17d4786",
"eventValue": "760117762236807093052",
"decimalAdjustedEventValue": "760.1177622368072",
"tokenPriceEventValue": "7.490623316382352",
"txHash": "0xe520661f1007dbd825a9d34dccf6ec7c28e5b7f72de9a23c8e962e67bb5ee9be",
"chainId": "1",
"contractAddress": "0x5a666c7d92e5fa7edcb6390e4efd6d0cdd69cf37",
"blockTime": "2021-03-30T15:56:06+00:00",
"id": "21"
},
{
"eventFrom": "0x9c5de3ad97b95a0da09fd0de84c347db450cd75c",
"eventTo": "0x8be54c4599c4f393026c9f41cf6bcf9fd66b10f7",
"eventValue": "51137087557317211323",
"decimalAdjustedEventValue": "51.137087557317216",
"tokenPriceEventValue": "7.362847571576029",
"txHash": "0xcf2f81f81d38e6dfcf30d0958e3faf936259442106eddbbf1f0de546e67e1d9b",
"chainId": "1",
"contractAddress": "0x5a666c7d92e5fa7edcb6390e4efd6d0cdd69cf37",
"blockTime": "2021-03-30T15:55:56+00:00",
"id": "22"
},
.
.
.
.
{
"eventFrom": "0x9c5de3ad97b95a0da09fd0de84c347db450cd75c",
"eventTo": "0x34dccfae06202d1167797870679fc1fb9ef7a523",
"eventValue": "453363841465952335411",
"decimalAdjustedEventValue": "453.3638414659523",
"tokenPriceEventValue": "7.704793717011617",
"txHash": "0xfbeaf03973d4e69b71a266abca106726e8b36549cc67806e19827890b886798c",
"chainId": "1",
"contractAddress": "0x5a666c7d92e5fa7edcb6390e4efd6d0cdd69cf37",
"blockTime": "2021-03-30T15:55:19+00:00",
"id": "29"
},
{
"eventFrom": "0x9c5de3ad97b95a0da09fd0de84c347db450cd75c",
"eventTo": "0xe9cc4e6bdd4b722930a09af9bb99d6726cf45b31",
"eventValue": "96575600000000000000",
"decimalAdjustedEventValue": "96.5756",
"tokenPriceEventValue": "7.704793717011617",
"txHash": "0x1adeb1878252ac4798e4be26a4e5fae99152a6b689818c5ae5966b793aad7965",
"chainId": "1",
"contractAddress": "0x5a666c7d92e5fa7edcb6390e4efd6d0cdd69cf37",
"blockTime": "2021-03-30T15:55:19+00:00",
"id": "30"
}
]
}
}
}

Sorting

Pagination without an obvious sort is rarely ever useful. In this case, let’s try and sort by the blockTime . Let’s say we want the latest transactions first then orderBy is the filter to use. It accepts an enum that’s self explanatory in function and orders our data.

query TransferData {
allTransferEvents(orderBy: BLOCK_TIME_DESC) {
nodes {
eventFrom
eventTo
eventValue
decimalAdjustedEventValue
tokenPriceEventValue
txHash
chainId
contractAddress
blockTime
}
}
}

Plugins

Pagination will likely not be the only filter you’d apply to your data. You may need a host of other filters and conditions to effectively parse your data.

Condition

This plugin allows you to make equality-based filters when querying data. Say you want only a particular transaction hash, your query would look as follows:

query TransferData {
allTransferEvents(
condition: {txHash: "0xca87ce37910bd443d01f335be03247bf8f184203c2c42860976f03e1c2e7c6ab"}
) {
nodes {
eventFrom
eventTo
eventValue
decimalAdjustedEventValue
tokenPriceEventValue
txHash
chainId
contractAddress
}
}
}
{
"data": {
"allTransferEvents": {
"nodes": [
{
"eventFrom": "0xeba6d5b21dd4fffbd31a8719dd73eaced887b413",
"eventTo": "0xf13f96f7f95517ae54a0b3f9494e58607edfca1f",
"eventValue": "600000000000000000000000",
"decimalAdjustedEventValue": "600000",
"tokenPriceEventValue": "0",
"txHash": "0xca87ce37910bd443d01f335be03247bf8f184203c2c42860976f03e1c2e7c6ab",
"chainId": "1",
"contractAddress": "0x5a666c7d92e5fa7edcb6390e4efd6d0cdd69cf37"
}
]
}
}
}

Filter

Filter can be considered a superset of Condition. It allows you to make any logical comparison. Maybe you only care about transactions which have more than 8000 tokens transferred.

query TransferData {
allTransferEvents(
filter: {decimalAdjustedEventValue: {greaterThanOrEqualTo: "8000"}}
) {
nodes {
eventFrom
eventTo
eventValue
decimalAdjustedEventValue
tokenPriceEventValue
txHash
chainId
contractAddress
}
}
}
query TransferData {
allTransferEvents(
filter: {
decimalAdjustedEventValue: { greaterThanOrEqualTo: "8000" }
and: { tokenPriceEventValue: { greaterThan: "1.6" } }
}
) {
nodes {
eventFrom
eventTo
eventValue
decimalAdjustedEventValue
tokenPriceEventValue
txHash
chainId
contractAddress
}
}
}

Advanced Querying

There will likely be scenarios where you need data from multiple tables within the same context. You can request data from multiple different tables or even different kinds of data from the same table and submit just a single query.

query TransferAndApprovalData {
allTransferEvents(
condition: { eventFrom: "0x90b32e5408b9ea6336472c8367dfd349a0c28c28" }
) {
nodes {
eventFrom
eventTo
eventValue
decimalAdjustedEventValue
tokenPriceEventValue
txHash
chainId
contractAddress
}
}
allApprovalEvents(
condition: { eventOwner: "0x90b32e5408b9ea6336472c8367dfd349a0c28c28" }
) {
nodes {
eventOwner
eventSpender
eventValue
txHash
chainId
contractAddress
}
}
}
{
"data": {
"allTransferEvents": {
"nodes": [
{
"eventFrom": "0x90b32e5408b9ea6336472c8367dfd349a0c28c28",
"eventTo": "0x9c5de3ad97b95a0da09fd0de84c347db450cd75c",
"eventValue": "8000032599896102303526",
"decimalAdjustedEventValue": "8000.032599896102",
"tokenPriceEventValue": "5.131407150879911",
"txHash": "0x7f8b91414e74afa25c4bcbcec295123d52a64a3f3344792fc8818ca6eedad3e7",
"chainId": "1",
"contractAddress": "0x5a666c7d92e5fa7edcb6390e4efd6d0cdd69cf37"
},
{
"eventFrom": "0x90b32e5408b9ea6336472c8367dfd349a0c28c28",
"eventTo": "0x9c5de3ad97b95a0da09fd0de84c347db450cd75c",
"eventValue": "6000000000000000000000",
"decimalAdjustedEventValue": "6000",
"tokenPriceEventValue": "5.241203173168192",
"txHash": "0xd5e526527f5a84f35abacc434dceb4e8895363e733c4fb10df3e92b9651b9bdd",
"chainId": "1",
"contractAddress": "0x5a666c7d92e5fa7edcb6390e4efd6d0cdd69cf37"
},
{
"eventFrom": "0x90b32e5408b9ea6336472c8367dfd349a0c28c28",
"eventTo": "0x9c5de3ad97b95a0da09fd0de84c347db450cd75c",
"eventValue": "6632550023971604865425",
"decimalAdjustedEventValue": "6632.550023971605",
"tokenPriceEventValue": "5.418116584137487",
"txHash": "0x39f7facc6a958e8bc1c3accfe59ee28bba2a99d50696c8d0222b7599a0330a4f",
"chainId": "1",
"contractAddress": "0x5a666c7d92e5fa7edcb6390e4efd6d0cdd69cf37"
}
]
},
"allApprovalEvents": {
"nodes": [
{
"eventOwner": "0x90b32e5408b9ea6336472c8367dfd349a0c28c28",
"eventSpender": "0x7a250d5630b4cf539739df2c5dacb4c659f2488d",
"eventValue": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"txHash": "0xde43a70c0698a74beea13daa78580139d620c2e38a24b81c76bc8974d15fc6b4",
"chainId": "1",
"contractAddress": "0x5a666c7d92e5fa7edcb6390e4efd6d0cdd69cf37"
},
{
"eventOwner": "0x90b32e5408b9ea6336472c8367dfd349a0c28c28",
"eventSpender": "0x7a250d5630b4cf539739df2c5dacb4c659f2488d",
"eventValue": "115792089237316195423570985008687907853269984665640564031457551408017027336409",
"txHash": "0x7f8b91414e74afa25c4bcbcec295123d52a64a3f3344792fc8818ca6eedad3e7",
"chainId": "1",
"contractAddress": "0x5a666c7d92e5fa7edcb6390e4efd6d0cdd69cf37"
},
{
"eventOwner": "0x90b32e5408b9ea6336472c8367dfd349a0c28c28",
"eventSpender": "0x7a250d5630b4cf539739df2c5dacb4c659f2488d",
"eventValue": "115792089237316195423570985008687907853269984665640564025457551408017027336409",
"txHash": "0xd5e526527f5a84f35abacc434dceb4e8895363e733c4fb10df3e92b9651b9bdd",
"chainId": "1",
"contractAddress": "0x5a666c7d92e5fa7edcb6390e4efd6d0cdd69cf37"
},
{
"eventOwner": "0x90b32e5408b9ea6336472c8367dfd349a0c28c28",
"eventSpender": "0x7a250d5630b4cf539739df2c5dacb4c659f2488d",
"eventValue": "115792089237316195423570985008687907853269984665640564018825001384045422470984",
"txHash": "0x39f7facc6a958e8bc1c3accfe59ee28bba2a99d50696c8d0222b7599a0330a4f",
"chainId": "1",
"contractAddress": "0x5a666c7d92e5fa7edcb6390e4efd6d0cdd69cf37"
}
]
}
}
}

Query Batching

You could also batch the queries together in your request and submit it as a batched query request.

[
{
"query": "query TransferData {\n allTransferEvents(\n condition: { eventFrom: \"0x90b32e5408b9ea6336472c8367dfd349a0c28c28\" }\n ) {\n nodes {\n eventFrom\n eventTo\n eventValue\n decimalAdjustedEventValue\n tokenPriceEventValue\n txHash\n chainId\n contractAddress\n }\n }\n}",
"operationName": "TransferData"
},
{
"query": "query ApprovalEvents{allApprovalEvents(\n condition: {eventOwner: \"0x90b32e5408b9ea6336472c8367dfd349a0c28c28\"}\n ) {\n nodes {\n eventOwner\n eventSpender\n eventValue\n txHash\n chainId\n contractAddress\n }\n }\n}",
"operationName": "ApprovalEvents"
}
]
curl --location --request POST 'https://dep-jekbi9rdfyi7tuqw-graphql.prod.unmarshal.com/marshether_oklzj/graphql' \
--header 'Content-Type: application/json' \
--data-raw '[
{
"query": "query TransferData {\n allTransferEvents(\n condition: { eventFrom: \"0x90b32e5408b9ea6336472c8367dfd349a0c28c28\" }\n ) {\n nodes {\n eventFrom\n eventTo\n eventValue\n decimalAdjustedEventValue\n tokenPriceEventValue\n txHash\n chainId\n contractAddress\n }\n }\n}",
"operationName": "TransferData"
},
{
"query": "query ApprovalEvents{allApprovalEvents(\n condition: {eventOwner: \"0x90b32e5408b9ea6336472c8367dfd349a0c28c28\"}\n ) {\n nodes {\n eventOwner\n eventSpender\n eventValue\n txHash\n chainId\n contractAddress\n }\n }\n}",
"operationName": "ApprovalEvents"
}
]'

Edges and Fragments

When writing deep and large enough queries you might have situation where the same node is queried multiple times. In these cases, edges and their accompanying Fragments may be important.

query TransferData {
userInFrom: allTransferEvents(
condition: {eventFrom: "0x90b32e5408b9ea6336472c8367dfd349a0c28c28"}
) {
edges {
...TransferEventsEdgeFragment
}
}
userInTo: allTransferEvents(
condition: {eventTo: "0x90b32e5408b9ea6336472c8367dfd349a0c28c28"}
) {
edges {
...TransferEventsEdgeFragment
}
}
}
fragment TransferEventsEdgeFragment on TransferEventsEdge {
node {
eventFrom
eventTo
eventValue
txHash
decimalAdjustedEventValue
tokenPriceEventValue
}
}

About Unmarshal

Unmarshal is a Multi-chain Web 3.0 data network aiming to deliver granular, reliable & real-time data to dApps, DeFi protocols, NFTs, Metaverse and GameFi solutions. Unmarshal provides the easiest way to query Blockchain data from Ethereum, Polygon, BNB Chain, Avalanche, Fantom, Celo, Solana, Klaytn, Kadena, Arbitrum, and XDC Network. Unmarshal network consists of data indexers and transforming tools to power Web 3.0 applications on any chain while providing a latent view of transformed data.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store