Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ var rootCmd = &cobra.Command{
Long: "A command-line interface for the Dune Analytics platform.\n\n" +
"Discover datasets across the Dune catalog, execute SQL queries (DuneSQL dialect),\n" +
"retrieve execution results, and manage your saved queries — all from the terminal.\n\n" +
"Capabilities:\n" +
" - Search datasets by keyword, contract address, category, or blockchain\n" +
" - Create, update, archive, and retrieve saved DuneSQL queries\n" +
" - Execute saved queries or raw DuneSQL and display results\n" +
" - Browse Dune documentation for DuneSQL syntax, API references, and guides\n" +
" - Monitor credit usage, storage consumption, and billing periods\n\n" +
"Authenticate with an API key via --api-key, the DUNE_API_KEY environment variable,\n" +
"or by running `dune auth`.",
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
Expand Down
6 changes: 5 additions & 1 deletion cmd/dataset/dataset.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ import "github.com/spf13/cobra"
func NewDatasetCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "dataset",
Short: "Discover and explore datasets across the Dune catalog",
Short: "Search and discover Dune datasets",
Long: "Search the Dune dataset catalog to discover tables and their schemas.\n\n" +
"Use 'dataset search' for keyword-based discovery across all tables, or\n" +
"'dataset search-by-contract' to find decoded tables for a specific contract address.",
}
cmd.AddCommand(newSearchCmd())
cmd.AddCommand(newSearchByContractCmd())
return cmd
}
27 changes: 19 additions & 8 deletions cmd/dataset/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,34 @@ func newSearchCmd() *cobra.Command {
Short: "Search for tables and datasets across the Dune catalog",
Long: "Natural-language table discovery across the Dune catalog. Use this command\n" +
"to find concrete table names for use in SQL queries.\n\n" +
"Filter by category (canonical for chain primitives, decoded for ABI-level\n" +
"events/calls, spell for curated datasets, community for user-contributed),\n" +
"by blockchain, schema, dataset type, or ownership scope.",
RunE: runSearch,
"The catalog includes:\n" +
" - canonical: chain-native primitives (blocks, transactions, logs, traces)\n" +
" - decoded: ABI-level events and function calls parsed from contract interactions\n" +
" - spell: curated Spellbook transformation datasets (e.g. dex.trades)\n" +
" - community: user-contributed tables and uploads\n\n" +
"Filter by category, blockchain, schema, dataset type, or ownership scope.\n" +
"Use --include-schema to get column names and types for SQL generation.\n\n" +
"For contract-specific lookup, use 'dune dataset search-by-contract' instead.\n\n" +
"Examples:\n" +
" dune dataset search --query \"uniswap swaps\"\n" +
" dune dataset search --query \"transfers\" --categories decoded --blockchains ethereum\n" +
" dune dataset search --query \"dex trades\" --categories spell --include-schema --output json\n" +
" dune dataset search --query \"*\" --owner-scope me",
RunE: runSearch,
}

cmd.Flags().String("query", "", "natural-language search intent or entity hints (e.g. 'uniswap v3 swaps'); use '*' to browse without keyword bias")
cmd.Flags().StringArray("categories", nil, "filter by table family: canonical (chain primitives), decoded (ABI-level events/calls), spell (curated datasets), community (user-contributed)")
cmd.Flags().StringArray("blockchains", nil, "chain scope to reduce ambiguity and improve ranking (e.g. ethereum, solana)")
cmd.Flags().StringArray("dataset-types", nil, "fine-grained dataset type filter: dune_table, decoded_table, spell, uploaded_table, transformation_table, transformation_view")
cmd.Flags().StringArray("blockchains", nil, "chain scope to reduce ambiguity and improve ranking (e.g. ethereum, solana, arbitrum)")
cmd.Flags().StringArray("dataset-types", nil,
"fine-grained dataset type filter: dune_table, decoded_table, spell, uploaded_table, transformation_table, transformation_view")
cmd.Flags().StringArray("schemas", nil, "schema/namespace constraint for high precision (e.g. dex, uniswap_v3_ethereum)")
cmd.Flags().String("owner-scope", "", "ownership filter: all, me, or team; does NOT automatically include private datasets")
cmd.Flags().String("owner-scope", "", "ownership filter: all (default), me, or team; does NOT automatically include private datasets")
cmd.Flags().Bool("include-private", false, "widen results to include private datasets visible to the authenticated user/team alongside public ones")
cmd.Flags().Bool("include-schema", false, "include column-level schema (name, type, nullable) for every result; useful when preparing SQL")
cmd.Flags().Bool("include-metadata", false, "include category-specific metadata (page_rank_score, description, abi_type, contract_name, project_name, etc.)")
cmd.Flags().Int32("limit", 20, "number of results per page; use 5-15 for quick checks, 20-50 for deeper exploration")
cmd.Flags().Int32("offset", 0, "pagination offset; use previous response pagination info for next page")
cmd.Flags().Int32("offset", 0, "pagination offset; combine with limit to page through large result sets")
output.AddFormatFlag(cmd, "text")

return cmd
Expand Down
116 changes: 116 additions & 0 deletions cmd/dataset/search_by_contract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package dataset

import (
"fmt"
"strings"

"github.com/duneanalytics/cli/cmdutil"
"github.com/duneanalytics/cli/output"
"github.com/duneanalytics/duneapi-client-go/models"
"github.com/spf13/cobra"
)

func newSearchByContractCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "search-by-contract",
Short: "Search for decoded tables associated with a specific contract address",
Long: `Search for decoded tables (events and calls) associated with a specific contract address.

This command finds all available decoded tables for a given smart contract address.
Decoded tables contain blockchain data that has been parsed according to a contract's ABI,
providing a structured view of contract interactions.

Table types returned:
- Event tables: contain parsed event logs emitted by smart contracts
- Call tables: contain parsed function calls made to smart contracts

When to use this command:
- You have a specific contract address (EVM or Tron) and want to find its decoded tables
- You want to analyze on-chain activity for a particular smart contract
- You need to find event or call tables for a known contract

For broader table discovery by keyword or project name, use 'dune dataset search' instead.

Examples:
dune dataset search-by-contract --contract-address 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984
dune dataset search-by-contract --contract-address 0x1f98... --blockchains ethereum --blockchains arbitrum
dune dataset search-by-contract --contract-address 0x1f98... --include-schema
dune dataset search-by-contract --contract-address 0x1f98... --limit 50 --offset 20`,
RunE: runSearchByContract,
}

cmd.Flags().String("contract-address", "",
"The contract address to search for. Accepts EVM addresses (starting with 0x) or Tron addresses. "+
"Example: '0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984'.")
_ = cmd.MarkFlagRequired("contract-address")
cmd.Flags().StringArray("blockchains", nil,
"Filter results to specific blockchains (e.g. ethereum, arbitrum). "+
"Can be specified multiple times. If not provided, searches across all blockchains where the contract exists.")
cmd.Flags().Bool("include-schema", false,
"If set, include the column-level schema (name, type, nullable) for every result. "+
"Enable when preparing SQL generation.")
cmd.Flags().Int32("limit", 20,
"Maximum number of results to return. "+
"Use smaller values (5-10) for quick checks, larger values (50-100) for comprehensive discovery.")
cmd.Flags().Int32("offset", 0,
"Number of results to skip for pagination. Use for paginating through large result sets.")
output.AddFormatFlag(cmd, "text")

return cmd
}

func runSearchByContract(cmd *cobra.Command, _ []string) error {
client := cmdutil.ClientFromCmd(cmd)

contractAddress, _ := cmd.Flags().GetString("contract-address")

req := models.SearchDatasetsByContractAddressRequest{
ContractAddress: contractAddress,
}

if cmd.Flags().Changed("blockchains") {
v, _ := cmd.Flags().GetStringArray("blockchains")
req.Blockchains = v
}
if cmd.Flags().Changed("include-schema") {
v, _ := cmd.Flags().GetBool("include-schema")
req.IncludeSchema = &v
}
if cmd.Flags().Changed("limit") {
v, _ := cmd.Flags().GetInt32("limit")
req.Limit = &v
}
if cmd.Flags().Changed("offset") {
v, _ := cmd.Flags().GetInt32("offset")
req.Offset = &v
}

resp, err := client.SearchDatasetsByContractAddress(req)
if err != nil {
return err
}

w := cmd.OutOrStdout()
switch output.FormatFromCmd(cmd) {
case output.FormatJSON:
return output.PrintJSON(w, resp)
default:
columns := []string{"FULL_NAME", "CATEGORY", "DATASET_TYPE", "BLOCKCHAINS"}
rows := make([][]string, len(resp.Results))
for i, r := range resp.Results {
dt := ""
if r.DatasetType != nil {
dt = *r.DatasetType
}
rows[i] = []string{
r.FullName,
r.Category,
dt,
strings.Join(r.Blockchains, ", "),
}
}
output.PrintTable(w, columns, rows)
fmt.Fprintf(w, "\n%d of %d results\n", len(resp.Results), resp.Total)
return nil
}
Comment thread
ivpusic marked this conversation as resolved.
}
18 changes: 14 additions & 4 deletions cmd/docs/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,25 @@ const defaultMCPEndpoint = "https://docs.dune.com/mcp"

func newSearchCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "search",
Short: "Search the Dune documentation for guides, API references, and code examples",
Use: "search",
Short: "Search the Dune documentation for guides, API references, and code examples",
Long: "Search across all Dune documentation pages including guides, API references,\n" +
"DuneSQL syntax, and code examples. Does not require authentication.",
"DuneSQL syntax, table naming conventions, and code examples.\n" +
"Does not require authentication.\n\n" +
"Useful for looking up:\n" +
" - DuneSQL functions and syntax (date/time, string, aggregate, window functions)\n" +
" - API endpoint references and authentication\n" +
" - Table naming conventions and dataset structure\n" +
" - Dune platform concepts (decoding, Spellbook, materialized views)\n\n" +
"Examples:\n" +
" dune docs search --query \"DuneSQL string functions\"\n" +
" dune docs search --query \"execute query API\" --api-reference-only\n" +
" dune docs search --query \"decoded tables\" --code-only",
Annotations: map[string]string{"skipAuth": "true"},
RunE: runSearch,
}

cmd.Flags().String("query", "", "search query text, e.g. 'DuneSQL date functions' or 'API authentication' (required)")
cmd.Flags().String("query", "", "natural-language search query for Dune docs; e.g. 'DuneSQL date functions', 'API authentication', 'decoded tables meaning', 'query pagination' (required)")
_ = cmd.MarkFlagRequired("query")
cmd.Flags().Bool("api-reference-only", false, "prioritize API reference pages over conceptual guides")
cmd.Flags().Bool("code-only", false, "prioritize pages with executable examples and code snippets")
Expand Down
4 changes: 4 additions & 0 deletions cmd/execution/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ func NewExecutionCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "execution",
Short: "Retrieve and inspect query execution results",
Long: "Retrieve and inspect the results of query executions.\n\n" +
"Use 'execution results <execution-id>' to fetch results from a previously\n" +
"submitted query execution. The execution ID is returned when running queries\n" +
"with 'dune query run --no-wait' or 'dune query run-sql --no-wait'.",
}
cmd.AddCommand(newResultsCmd())
return cmd
Expand Down
22 changes: 15 additions & 7 deletions cmd/execution/results.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,31 @@ var PollInterval = 2 * time.Second
func newResultsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "results <execution-id>",
Short: "Get execution results for a query execution by execution ID",
Short: "Retrieve execution results for a query execution by execution ID",
Long: "Retrieve the results of a query execution. By default, waits for the execution\n" +
"to complete (up to the timeout) before returning results.\n\n" +
"Behavior:\n" +
" 1. Checks the current execution status\n" +
" 2. If still running: polls until complete or timeout is reached\n" +
" 2. If still running: polls every 2 seconds until complete or timeout is reached\n" +
" 3. If completed: returns the result data\n" +
" 4. If failed/cancelled: returns the error details\n\n" +
"Use --no-wait to return the current state immediately without polling.",
"Use --no-wait to return the current state immediately without polling.\n" +
"Use --limit and --offset to paginate through large result sets.\n\n" +
"Use this after submitting a query with --no-wait, or to re-fetch results from\n" +
"a completed execution.\n\n" +
"Examples:\n" +
" dune execution results 01JXYZ...\n" +
" dune execution results 01JXYZ... --limit 100 --offset 200\n" +
" dune execution results 01JXYZ... --no-wait\n" +
" dune execution results 01JXYZ... --output json",
Args: cobra.ExactArgs(1),
RunE: runResults,
}

cmd.Flags().Int("limit", 0, "maximum number of result rows to return (0 = all)")
cmd.Flags().Int("offset", 0, "number of rows to skip before returning results, used for pagination")
cmd.Flags().Bool("no-wait", false, "return the current execution state immediately without waiting for completion")
cmd.Flags().Int("timeout", 300, "maximum seconds to wait for the execution to complete before timing out")
cmd.Flags().Int("limit", 0, "maximum number of result rows to return (0 = all); use with --offset to paginate large result sets")
cmd.Flags().Int("offset", 0, "number of rows to skip before starting to return results; use with --limit for pagination through large result sets")
cmd.Flags().Bool("no-wait", false, "return the current execution state immediately without waiting for completion; useful for checking status of long-running queries")
cmd.Flags().Int("timeout", 300, "maximum seconds to wait for the execution to complete before timing out (default 300s = 5 minutes)")
output.AddFormatFlag(cmd, "text")

return cmd
Expand Down
14 changes: 10 additions & 4 deletions cmd/query/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,26 @@ import (
func newCreateCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "create",
Short: "Create a new Dune query and return the query ID",
Short: "Create a new saved Dune query and return the query ID",
Long: "Create a new SQL query on Dune. Returns the query ID on success.\n\n" +
"The query is written in DuneSQL dialect. If the query targets tables with\n" +
"known partition columns, include a WHERE filter on those columns\n" +
"(e.g. WHERE block_date >= CURRENT_DATE - INTERVAL '7' DAY) to enable\n" +
"partition pruning and reduce query cost.",
RunE: runCreate,
"partition pruning and reduce query cost.\n\n" +
"Use --temp to create a temporary query that won't appear in the dune.com\n" +
"library or be accessible when shared. Temporary queries are auto-deleted.\n\n" +
"Examples:\n" +
" dune query create --name \"Recent Blocks\" --sql \"SELECT * FROM ethereum.blocks LIMIT 10\"\n" +
" dune query create --name \"My Query\" --sql \"SELECT 1\" --private --description \"Test query\"\n" +
" dune query create --name \"Throwaway\" --sql \"SELECT 1\" --temp",
RunE: runCreate,
}

cmd.Flags().String("name", "", "human-readable query title, max 600 characters (required)")
cmd.Flags().String("sql", "", "the SQL query text in DuneSQL dialect, max 500,000 characters (required)")
cmd.Flags().String("description", "", "short description of what the query does, max 1,000 characters")
cmd.Flags().Bool("private", false, "make the query private; may be forced by team privacy settings")
cmd.Flags().Bool("temp", false, "create a temporary query that won't appear in the dune.com library or be accessible when shared")
cmd.Flags().Bool("temp", false, "create a temporary query that won't appear in the dune.com library or be accessible when shared; auto-deleted")
_ = cmd.MarkFlagRequired("name")
_ = cmd.MarkFlagRequired("sql")
output.AddFormatFlag(cmd, "text")
Expand Down
8 changes: 8 additions & 0 deletions cmd/query/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ func NewQueryCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "query",
Short: "Create, retrieve, update, execute, and archive Dune queries",
Long: "Create, retrieve, update, archive, and execute DuneSQL queries.\n\n" +
"Subcommands:\n" +
" create - Save a new reusable DuneSQL query and get its query ID\n" +
" get - Fetch a saved query's SQL, metadata, and execution state\n" +
" update - Modify a query's SQL, title, description, privacy, or tags\n" +
" archive - Hide a query from the library (still retrievable by ID)\n" +
" run - Execute a saved query by ID and display results\n" +
" run-sql - Execute raw DuneSQL inline without saving a query",
}
cmd.AddCommand(newCreateCmd())
cmd.AddCommand(newGetCmd())
Expand Down
26 changes: 18 additions & 8 deletions cmd/query/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,28 @@ func newRunCmd() *cobra.Command {
Use: "run <query-id>",
Short: "Execute a saved Dune query by its ID and display results",
Long: "Execute a saved Dune query by its numeric ID. By default, waits for the\n" +
"execution to complete and displays the result rows. Use --no-wait to submit\n" +
"the execution and exit immediately with just the execution ID.\n\n" +
"execution to complete (polling every 2 seconds) and displays the result rows.\n" +
"Use --no-wait to submit the execution and exit immediately with just the\n" +
"execution ID; then fetch results later with 'dune execution results <execution-id>'.\n\n" +
"Credits are consumed based on actual compute resources used. Use --performance\n" +
"to select the engine size (medium or large).",
Args: cobra.ExactArgs(1),
RunE: runRun,
"to select the engine size (medium or large).\n\n" +
"Important: if the query targets tables with known partition columns (returned by\n" +
"'dune dataset search' or 'dune dataset search-by-contract'), ensure the SQL includes\n" +
"a WHERE filter on those partition columns (e.g. WHERE block_date >= CURRENT_DATE -\n" +
"INTERVAL '7' DAY). This enables partition pruning and significantly reduces query cost.\n\n" +
"Examples:\n" +
" dune query run 12345\n" +
" dune query run 12345 --param wallet=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --param days=30\n" +
" dune query run 12345 --performance large --limit 100\n" +
" dune query run 12345 --no-wait\n" +
" dune query run 12345 --timeout 600",
Args: cobra.ExactArgs(1),
RunE: runRun,
}

cmd.Flags().StringArray("param", nil, "typed query parameter in key=value format (repeatable); numbers are stringified, datetimes use YYYY-MM-DD HH:mm:ss")
cmd.Flags().StringArray("param", nil, "typed query parameter in key=value format (repeatable); supported types: text, number (stringified, e.g. '30'), datetime (YYYY-MM-DD HH:mm:ss), enum")
cmd.Flags().String("performance", "medium", `engine size for the execution: "medium" (default) or "large"; credits are consumed based on actual compute resources used`)
cmd.Flags().Int("limit", 0, "maximum number of result rows to return (0 = all)")
cmd.Flags().Int("limit", 0, "maximum number of result rows to return (0 = all available rows)")
cmd.Flags().Bool("no-wait", false, "submit the execution and exit immediately, printing only the execution ID and state")
cmd.Flags().Int("timeout", 300, "maximum seconds to wait for the execution to complete before timing out")
output.AddFormatFlag(cmd, "text")
Expand Down Expand Up @@ -106,4 +117,3 @@ func parseParams(raw []string) (map[string]any, error) {
}
return params, nil
}

Loading