Skip to content

Commit 248e655

Browse files
authored
Add sim evm supported-protocols command (#52)
Wires up GET /v1/evm/defi/supported-protocols as `dune sim evm supported-protocols`, with --include-preview-chains / --include-preview-protocols flags and text + JSON output. The text table lists each protocol family, its chains (with '*' marking preview status) and recognized sub-protocols, letting users discover what the defi-positions endpoint can return. Closes GRO-227.
1 parent 6abb50f commit 248e655

4 files changed

Lines changed: 199 additions & 2 deletions

File tree

cmd/sim/evm/evm.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ func NewEvmCmd() *cobra.Command {
4646
" collectibles - ERC721 and ERC1155 NFT holdings with spam filtering\n" +
4747
" token-info - Token metadata, pricing, supply, and market cap\n" +
4848
" token-holders - Top holders of an ERC20 token ranked by balance\n" +
49-
" defi-positions - DeFi positions across lending, AMM, and vault protocols (beta)\n\n" +
49+
" defi-positions - DeFi positions across lending, AMM, and vault protocols (beta)\n" +
50+
" supported-protocols - DeFi protocol families and chains covered by defi-positions\n\n" +
5051
"Most commands support --chain-ids to restrict results to specific networks.\n" +
5152
"Run 'dune sim evm supported-chains' to discover valid chain IDs, tags, and\n" +
5253
"which endpoints are available per chain.\n\n" +
@@ -63,6 +64,7 @@ func NewEvmCmd() *cobra.Command {
6364
cmd.AddCommand(NewTokenInfoCmd())
6465
cmd.AddCommand(NewTokenHoldersCmd())
6566
cmd.AddCommand(NewDefiPositionsCmd())
67+
cmd.AddCommand(NewSupportedProtocolsCmd())
6668

6769
return cmd
6870
}

cmd/sim/evm/supported_protocols.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package evm
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/url"
7+
"strings"
8+
9+
"github.com/spf13/cobra"
10+
11+
"github.com/duneanalytics/cli/output"
12+
)
13+
14+
// Chain-status values returned by the API are lowercase strings.
15+
const protocolStatusPreview = "preview"
16+
17+
// NewSupportedProtocolsCmd returns the `sim evm supported-protocols` command.
18+
func NewSupportedProtocolsCmd() *cobra.Command {
19+
cmd := &cobra.Command{
20+
Use: "supported-protocols",
21+
Short: "List DeFi protocol families and chains supported by defi-positions",
22+
Long: "Display DeFi protocol families covered by the Sim defi-positions\n" +
23+
"endpoint, the chains each family is available on, and the sub-protocols\n" +
24+
"(forks) recognized under each family. Each chain entry has a status of\n" +
25+
"Stable or Preview.\n\n" +
26+
"Use this to discover which protocols and chains 'dune sim evm defi-positions'\n" +
27+
"can return data for.\n\n" +
28+
"Examples:\n" +
29+
" dune sim evm supported-protocols\n" +
30+
" dune sim evm supported-protocols --include-preview-chains\n" +
31+
" dune sim evm supported-protocols --include-preview-protocols\n" +
32+
" dune sim evm supported-protocols -o json",
33+
RunE: runSupportedProtocols,
34+
}
35+
36+
cmd.Flags().Bool("include-preview-chains", false, "Include chains that are marked as preview (not yet publicly available)")
37+
cmd.Flags().Bool("include-preview-protocols", false, "Include protocols that are marked as preview on the requested chains")
38+
output.AddFormatFlag(cmd, "text")
39+
40+
return cmd
41+
}
42+
43+
type supportedProtocolsResponse struct {
44+
ProtocolFamilies []supportedProtocolFamily `json:"protocol_families"`
45+
}
46+
47+
type supportedProtocolFamily struct {
48+
Family string `json:"family"`
49+
Chains []supportedProtocolChain `json:"chains"`
50+
SubProtocols []string `json:"sub_protocols"`
51+
}
52+
53+
type supportedProtocolChain struct {
54+
ChainID json.Number `json:"chain_id"`
55+
ChainName string `json:"chain_name"`
56+
Status string `json:"status"`
57+
}
58+
59+
func runSupportedProtocols(cmd *cobra.Command, _ []string) error {
60+
client := SimClientFromCmd(cmd)
61+
if client == nil {
62+
return fmt.Errorf("sim client not initialized")
63+
}
64+
65+
params := url.Values{}
66+
if v, _ := cmd.Flags().GetBool("include-preview-chains"); v {
67+
params.Set("include_preview_chains", "true")
68+
}
69+
if v, _ := cmd.Flags().GetBool("include-preview-protocols"); v {
70+
params.Set("include_preview_protocols", "true")
71+
}
72+
73+
data, err := client.Get(cmd.Context(), "/v1/evm/defi/supported-protocols", params)
74+
if err != nil {
75+
return err
76+
}
77+
78+
w := cmd.OutOrStdout()
79+
switch output.FormatFromCmd(cmd) {
80+
case output.FormatJSON:
81+
var raw json.RawMessage = data
82+
return output.PrintJSON(w, raw)
83+
default:
84+
var resp supportedProtocolsResponse
85+
if err := json.Unmarshal(data, &resp); err != nil {
86+
return fmt.Errorf("parsing response: %w", err)
87+
}
88+
89+
columns := []string{"FAMILY", "CHAINS", "SUB_PROTOCOLS"}
90+
rows := make([][]string, len(resp.ProtocolFamilies))
91+
for i, f := range resp.ProtocolFamilies {
92+
rows[i] = []string{
93+
f.Family,
94+
formatProtocolChains(f.Chains),
95+
strings.Join(f.SubProtocols, ","),
96+
}
97+
}
98+
output.PrintTable(w, columns, rows)
99+
return nil
100+
}
101+
}
102+
103+
// formatProtocolChains renders chains as "name(id)" with a "*" suffix for
104+
// preview-status entries, joined by commas.
105+
func formatProtocolChains(chains []supportedProtocolChain) string {
106+
parts := make([]string, len(chains))
107+
for i, c := range chains {
108+
entry := fmt.Sprintf("%s(%s)", c.ChainName, c.ChainID.String())
109+
if strings.EqualFold(c.Status, protocolStatusPreview) {
110+
entry += "*"
111+
}
112+
parts[i] = entry
113+
}
114+
return strings.Join(parts, ",")
115+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package evm_test
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestEvmSupportedProtocols_Text(t *testing.T) {
13+
key := simAPIKey(t)
14+
15+
root := newSimTestRoot()
16+
var buf bytes.Buffer
17+
root.SetOut(&buf)
18+
root.SetArgs([]string{"sim", "--sim-api-key", key, "evm", "supported-protocols"})
19+
20+
require.NoError(t, root.Execute())
21+
22+
out := buf.String()
23+
assert.Contains(t, out, "FAMILY")
24+
assert.Contains(t, out, "CHAINS")
25+
assert.Contains(t, out, "SUB_PROTOCOLS")
26+
}
27+
28+
func TestEvmSupportedProtocols_JSON(t *testing.T) {
29+
key := simAPIKey(t)
30+
31+
root := newSimTestRoot()
32+
var buf bytes.Buffer
33+
root.SetOut(&buf)
34+
root.SetArgs([]string{"sim", "--sim-api-key", key, "evm", "supported-protocols", "-o", "json"})
35+
36+
require.NoError(t, root.Execute())
37+
38+
var resp map[string]interface{}
39+
require.NoError(t, json.Unmarshal(buf.Bytes(), &resp))
40+
assert.Contains(t, resp, "protocol_families")
41+
42+
families, ok := resp["protocol_families"].([]interface{})
43+
require.True(t, ok, "protocol_families should be an array")
44+
require.NotEmpty(t, families, "should have at least one protocol family")
45+
46+
first, ok := families[0].(map[string]interface{})
47+
require.True(t, ok)
48+
assert.Contains(t, first, "family")
49+
assert.Contains(t, first, "chains")
50+
assert.Contains(t, first, "sub_protocols")
51+
52+
chains, ok := first["chains"].([]interface{})
53+
require.True(t, ok, "chains should be an array")
54+
if len(chains) > 0 {
55+
c, ok := chains[0].(map[string]interface{})
56+
require.True(t, ok)
57+
assert.Contains(t, c, "chain_id")
58+
assert.Contains(t, c, "chain_name")
59+
assert.Contains(t, c, "status")
60+
}
61+
}
62+
63+
func TestEvmSupportedProtocols_IncludePreviewChainsFlag(t *testing.T) {
64+
key := simAPIKey(t)
65+
66+
root := newSimTestRoot()
67+
var buf bytes.Buffer
68+
root.SetOut(&buf)
69+
root.SetArgs([]string{
70+
"sim", "--sim-api-key", key, "evm", "supported-protocols",
71+
"--include-preview-chains", "-o", "json",
72+
})
73+
74+
require.NoError(t, root.Execute())
75+
76+
var resp map[string]interface{}
77+
require.NoError(t, json.Unmarshal(buf.Bytes(), &resp))
78+
assert.Contains(t, resp, "protocol_families")
79+
}

cmd/sim/sim.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ func NewSimCmd() *cobra.Command {
2727
"lookups.\n\n" +
2828
"Available subcommands:\n" +
2929
" evm - Query EVM chains: balances, activity, transactions, collectibles,\n" +
30-
" token-info, token-holders, defi-positions, supported-chains\n" +
30+
" token-info, token-holders, defi-positions, supported-chains,\n" +
31+
" supported-protocols\n" +
3132
" svm - Query SVM chains (Solana, Eclipse): balances, transactions\n" +
3233
" auth - Save your Sim API key to the local config file\n\n" +
3334
"Authentication:\n" +

0 commit comments

Comments
 (0)