/dunebuggy

Lightweight Python Client for Dune Analytics

Primary LanguageJupyter NotebookMIT LicenseMIT


Dunebuggy
Dunebuggy

A lightweight (unofficial) Python SDK for Dune.com

InstallationGetting startedRoadmapNotes

Installation

pip install dunebuggy

Getting started

Retrieving a public query

To retrieve a query, all we'll need is the query_id for the public query we're interested in. In the below example we can take a look at the popular "Custom NFT Floor Tracker" query by @smaroo (The query_id below can be found in the URL).

from dunebuggy import Dune

dune = Dune()
query = dune.fetch_query(83579)

query here is a DuneQuery object, we can get the pandas DataFrame for the query output bf calling df on the object

print(query.df.head())
Floor (Approx) Time Interval
0 0.122649 2021-06-01T00:00:00+00:00
1 0.130000 2021-06-02T00:00:00+00:00
2 0.193455 2021-06-03T00:00:00+00:00
3 0.189000 2021-06-04T00:00:00+00:00
4 0.189930 2021-06-05T00:00:00+00:00

We can also take a look at some basic information about the returned query with query.info

print(query.info)
{'name': 'Custom NFT Floor Tracker',
 'author': '@smaroo',
 'length': 264,
 'query_id': 83579,
 'result_id': UUID('e5aef8a0-1453-44d1-a27b-f576ea2b3ba2'),
 'job_id': UUID('ec680fa9-217f-44c5-b223-56730cd07473'),
 'columns': ['Time Interval', 'Floor (Approx)']}

Some queries in Dune are "parameterized", meaning that the author exposes certain variables for the user to enter custom values. The example query (83579) happens to be parameterized, we can verify this by inspecting query.parameters

print(query.parameters)
[QueryParameter(key='Enter NFT Contract Address', type='text', value='xc3f733ca98e0dad0386979eb96fb1722a1a05e69', enumOptions=None),
 QueryParameter(key='Floor Time Interval', type='enum', value='Day', enumOptions=['Day', 'Hour']),
 QueryParameter(key='Start Date', type='datetime', value='2021-06-01 00:00:00', enumOptions=None)]

If you'd like to run this query with your own custom parameters, all we'll need to do is take the parameters from from the initial query, change the values to what we want, and re-fetch the query.

Below we are replacing the old NFT contract address param with a new one (the contract address for BAYC)

params = query.parameters

# Replacing with contract address for BAYC
params[0].value = 'xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D'
custom_query = dune.fetch_query(83579, parameters=params)

Note: You can also create a fresh set of parameters by importing QueryParameter from dunebuggy.models.query and adding the values to the new object.

from dunebuggy.models.query import QueryParameter

param_to_change = QueryParameter(
  key='Enter NFT Contract Address',
  value='xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D',
)
params[0] = param_to_change
custom_query = dune.fetch_query(83579, parameters=params)
print(custom_query.info)
{'name': 'Custom NFT Floor Tracker',
 'author': '@smaroo',
 'length': 265,
 'query_id': 83579,
 'result_id': UUID('42a3c13d-5fbd-42bd-86c0-acc9adcdc803'),
 'job_id': UUID('9051ebe7-862f-46d0-9999-b4645659ca56'),
 'columns': ['Time Interval', 'Floor (Approx)']}

Note that the result_id and job_id here are different, this is because we ran the query with our changed params

print(custom_query.parameters)
[QueryParameter(key='Enter NFT Contract Address', type='text', value='xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D', enumOptions=None),
QueryParameter(key='Floor Time Interval', type='enum', value='Day', enumOptions=['Day', 'Hour']),
QueryParameter(key='Start Date', type='datetime', value='2021-06-01 00:00:00', enumOptions=None)]
print(custom_query.df.head())
Floor (Approx) Time Interval
0 0.8000 2021-06-01T00:00:00+00:00
1 0.8518 2021-06-02T00:00:00+00:00
2 0.8260 2021-06-03T00:00:00+00:00
3 0.7400 2021-06-04T00:00:00+00:00
4 0.8499 2021-06-05T00:00:00+00:00

Creating a new query

dunebuggy also allows you to create a new using an existing Dune.com account.To login just need to pass in your username/password into the Dune object.

You can verify your login by inspecting your Dune user_id

import os

username = os.environ.get('DUNE_USERNAME')
password = os.environ.get('DUNE_PASSWORD')

dune = Dune(username=username, password=password)
# print(dune.user_id)

To create a query now, all we need to do is pass in a name, query_string and dataset_id

We can construct the SQL query by using a raw sql string

query_string = "select * from ethereum.transactions\nLIMIT 100\n"

Or we could use a fancy ORM-style library like pypika

from pypika import Database, Query

ethereum = Database('ethereum')
q = Query.from_(ethereum.transactions).select('*').limit(100)
query_string = q.get_sql(quote_char=None)
print(query_string)
'SELECT * FROM ethereum.transactions LIMIT 100'

Dune requires us to specify a dataset_id for each of their supported blockchain datasets upon query creation. The currently supported datasets are the following:

Blockchain Dataset Id
ETHEREUM 4
XDAI 6
POLYGON 7
OPTIMISM_1 8
OPTIMISM_2 10
BINANCE 9
SOLANA 1

We can access these integer codes via the DatasetId enum

from dunebuggy.models.constants import DatasetId
created_query = dune.create_query("My Query's Name", query_string, DatasetId.ETHEREUM)

Our created query can be accessed like any other, you can also log into your Dune account as see it there as well!

print(created_query.df.head())
access_list block_hash block_number block_time data from gas_limit gas_price gas_used hash index max_fee_per_gas max_priority_fee_per_gas nonce priority_fee_per_gas success to type value
0 None \x887c665b0c52ccace092d817e984e2e828ef59079295... 47287 2015-08-07T08:50:01+00:00 None \xdb312d1d6a2ccc64dd94a3892928bac82b4e8c15 21000 100000000000 21000 \xd3e6a2fc34066d20bb83020b1ee95b9dc7919fd242bd... 0 None None 0 None None \x34bb6978c5a1ad68777ad388c6787df53903430c None 1000000000000000000
1 None \x4869e218b0a8f5784f16193ac66cbf35c4510ace0c9b... 48698 2015-08-07T15:29:53+00:00 None \x48040276e9c17ddbe5c8d2976245dcd0235efa43 90000 57550496008 21000 \x8ba39f908731171fe96ee4e700e71d170ef8e651fac7... 0 None None 0 None None \xd8d0549637b65d58e7fb6cbdd11530b399d1ddac None 100000000000000000000
2 None \xab9491b62b16bd928b281a83db82483584c22aeebc0d... 49051 2015-08-07T17:03:48+00:00 None \x8686578c4f7c75246f548299d6ffdac3b67b5cd1 90000 57178423039 21000 \x57f8ba638903d6335e211eb470159587c73316788880... 0 None None 0 None None \x87abffa6b80f712c852a9558120ba6611f0b5e46 None 45150000000000000000
3 None \x1f9adc2190701ca3085b28252e4f1f467d980f763dad... 49174 2015-08-07T17:41:03+00:00 None \x18e4ce47483b53040adbab35172c01ef64506e0c 90000 58589751415 21000 \xb8280da44f8d35011c3f431f7d1a82213477a4e742de... 2 None None 0 None None \xfb26ae2d3621829472555fbd11bb2a324b7a5c57 None 10000000000000000000
4 None \xf1f392fd197a149afe9f8843d7ba759d1a9f79d1ef62... 49938 2015-08-07T21:06:21+00:00 None \xc6bf5b6558f2ee21f2e43d9ff9b5408a0cb89413 90000 71214529679 21000 \x538e1664c12c55287c98dc5dd248f60c642cbbbd7a18... 0 None None 4 None None \x33a3f479f6c3e7f91128348490d1f7e8d2a0fab5 None 5000000000000000000

Saving to CSV

To save a query to a CSV, we can take advantage of the to_csv method on our df

created_query.df.to_csv('my_test_data.csv')

Roadmap

  • Cleanup punding TODO comments
  • Add support for embedding Dune graphs/ plotting w/ Dune style colors
  • Add tests (lol)
  • Add support for query updating
  • Investigate whether dashboard support makes sense?
  • Investigate whether there is a max row limit for data returned, if so, query in batches?
  • Better formatting for certain returned columns (links etc..)
  • Add Documentation (Sphinx or something else)

Notes

This project was inspired by the itzemstar's duneanalytics repo

README image is from the IAN Symbols dataset