Package: dunebuggy
DunebuggyA lightweight (unofficial) Python SDK forDune.comInstallation•Getting started•Roadmap•NotesInstallationpipinstalldunebuggyGetting startedRetrieving a public queryTo retrieve a query, all we'll need is thequery_idfor 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(Thequery_idbelow can be found in the URL).fromdunebuggyimportDunedune=Dune()query=dune.fetch_query(83579)queryhere is aDuneQueryobject, we can get thepandasDataFrame for the query output bf callingdfon the objectprint(query.df.head())Floor (Approx)Time Interval00.1226492021-06-01T00:00:00+00:0010.1300002021-06-02T00:00:00+00:0020.1934552021-06-03T00:00:00+00:0030.1890002021-06-04T00:00:00+00:0040.1899302021-06-05T00:00:00+00:00We can also take a look at some basic information about the returned query withquery.infoprint(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 inspectingquery.parametersprint(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 BAYCparams[0].value='xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D'custom_query=dune.fetch_query(83579,parameters=params)Note: You can also create a fresh set of parameters by importingQueryParameterfromdunebuggy.models.queryand adding the values to the new object.fromdunebuggy.models.queryimportQueryParameterparam_to_change=QueryParameter(key='Enter NFT Contract Address',value='xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D',)params[0]=param_to_changecustom_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 theresult_idandjob_idhere are different, this is because we ran the query with our changed paramsprint(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 Interval00.80002021-06-01T00:00:00+00:0010.85182021-06-02T00:00:00+00:0020.82602021-06-03T00:00:00+00:0030.74002021-06-04T00:00:00+00:0040.84992021-06-05T00:00:00+00:00Creating a new querydunebuggyalso allows you to create a new using an existing Dune.com account.To login just need to pass in your username/password into theDuneobject.You can verify your login by inspecting your Duneuser_idimportosusername=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 aname,query_stringanddataset_idWe can construct the SQL query by using a raw sql stringquery_string="select * from ethereum.transactions\nLIMIT 100\n"Or we could use a fancy ORM-style library likepypikafrompypikaimportDatabase,Queryethereum=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 adataset_idfor each of their supported blockchain datasets upon query creation. The currently supported datasets are the following:Blockchain DatasetIdETHEREUM4XDAI6POLYGON7OPTIMISM_18OPTIMISM_210BINANCE9SOLANA1We can access these integer codes via theDatasetIdenumfromdunebuggy.models.constantsimportDatasetIdcreated_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_listblock_hashblock_numberblock_timedatafromgas_limitgas_pricegas_usedhashindexmax_fee_per_gasmax_priority_fee_per_gasnoncepriority_fee_per_gassuccesstotypevalue0None\x887c665b0c52ccace092d817e984e2e828ef59079295...472872015-08-07T08:50:01+00:00None\xdb312d1d6a2ccc64dd94a3892928bac82b4e8c152100010000000000021000\xd3e6a2fc34066d20bb83020b1ee95b9dc7919fd242bd...0NoneNone0NoneNone\x34bb6978c5a1ad68777ad388c6787df53903430cNone10000000000000000001None\x4869e218b0a8f5784f16193ac66cbf35c4510ace0c9b...486982015-08-07T15:29:53+00:00None\x48040276e9c17ddbe5c8d2976245dcd0235efa43900005755049600821000\x8ba39f908731171fe96ee4e700e71d170ef8e651fac7...0NoneNone0NoneNone\xd8d0549637b65d58e7fb6cbdd11530b399d1ddacNone1000000000000000000002None\xab9491b62b16bd928b281a83db82483584c22aeebc0d...490512015-08-07T17:03:48+00:00None\x8686578c4f7c75246f548299d6ffdac3b67b5cd1900005717842303921000\x57f8ba638903d6335e211eb470159587c73316788880...0NoneNone0NoneNone\x87abffa6b80f712c852a9558120ba6611f0b5e46None451500000000000000003None\x1f9adc2190701ca3085b28252e4f1f467d980f763dad...491742015-08-07T17:41:03+00:00None\x18e4ce47483b53040adbab35172c01ef64506e0c900005858975141521000\xb8280da44f8d35011c3f431f7d1a82213477a4e742de...2NoneNone0NoneNone\xfb26ae2d3621829472555fbd11bb2a324b7a5c57None100000000000000000004None\xf1f392fd197a149afe9f8843d7ba759d1a9f79d1ef62...499382015-08-07T21:06:21+00:00None\xc6bf5b6558f2ee21f2e43d9ff9b5408a0cb89413900007121452967921000\x538e1664c12c55287c98dc5dd248f60c642cbbbd7a18...0NoneNone4NoneNone\x33a3f479f6c3e7f91128348490d1f7e8d2a0fab5None5000000000000000000Saving to CSVTo save a query to a CSV, we can take advantage of theto_csvmethod on ourdfcreated_query.df.to_csv('my_test_data.csv')RoadmapCleanup punding TODO commentsAdd support for embedding Dune graphs/ plotting w/ Dune style colorsAdd tests (lol)Add support for query updatingInvestigate 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)NotesThis project was inspired by theitzemstar's duneanalytics repoREADME image is from theIAN Symbols dataset