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
37 changes: 28 additions & 9 deletions cmd/sim/evm/balances.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,8 @@ func NewBalancesCmd() *cobra.Command {
RunE: runBalances,
}

cmd.Flags().String("chain-ids", "", "Comma-separated chain IDs or tags (default: all default chains)")
cmd.Flags().String("filters", "", "Token filter: erc20 or native")
addBalanceFlags(cmd)
cmd.Flags().String("asset-class", "", "Asset class filter: stablecoin")
cmd.Flags().String("metadata", "", "Extra metadata fields: logo,url,pools")
cmd.Flags().Bool("exclude-spam", false, "Exclude tokens with <100 USD liquidity")
cmd.Flags().String("historical-prices", "", "Hour offsets for historical prices (e.g. 720,168,24)")
cmd.Flags().Int("limit", 0, "Max results (1-1000)")
cmd.Flags().String("offset", "", "Pagination cursor from previous response")
output.AddFormatFlag(cmd, "text")

return cmd
}
Expand Down Expand Up @@ -70,6 +63,30 @@ type warningEntry struct {
}

func runBalances(cmd *cobra.Command, args []string) error {
return runBalancesEndpoint(cmd, args, "/v1/evm/balances/", "")
}

// addBalanceFlags registers the common flags shared by the balances and
// stablecoins commands.
func addBalanceFlags(cmd *cobra.Command) {
cmd.Flags().String("chain-ids", "", "Comma-separated chain IDs or tags (default: all default chains)")
cmd.Flags().String("filters", "", "Token filter: erc20 or native")
cmd.Flags().String("metadata", "", "Extra metadata fields: logo,url,pools")
cmd.Flags().Bool("exclude-spam", false, "Exclude tokens with <100 USD liquidity")
cmd.Flags().String("historical-prices", "", "Hour offsets for historical prices (e.g. 720,168,24)")
cmd.Flags().Int("limit", 0, "Max results (1-1000)")
cmd.Flags().String("offset", "", "Pagination cursor from previous response")
output.AddFormatFlag(cmd, "text")
}

// runBalancesEndpoint is the shared run implementation for the balances and
// stablecoins commands. The final API path is built as:
//
// pathPrefix + address + pathSuffix
//
// For example "/v1/evm/balances/" + addr + "" for balances,
// or "/v1/evm/balances/" + addr + "/stablecoins" for stablecoins.
func runBalancesEndpoint(cmd *cobra.Command, args []string, pathPrefix, pathSuffix string) error {
client := SimClientFromCmd(cmd)
if client == nil {
return fmt.Errorf("sim client not initialized")
Expand All @@ -84,6 +101,8 @@ func runBalances(cmd *cobra.Command, args []string) error {
if v, _ := cmd.Flags().GetString("filters"); v != "" {
params.Set("filters", v)
}
// asset-class is only registered on the balances command; silently ignored
// when the flag is absent.
if v, _ := cmd.Flags().GetString("asset-class"); v != "" {
params.Set("asset_class", v)
}
Expand All @@ -103,7 +122,7 @@ func runBalances(cmd *cobra.Command, args []string) error {
params.Set("offset", v)
}

data, err := client.Get(cmd.Context(), "/v1/evm/balances/"+address, params)
data, err := client.Get(cmd.Context(), pathPrefix+address+pathSuffix, params)
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions cmd/sim/evm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func NewEvmCmd() *cobra.Command {
cmd.AddCommand(NewSupportedChainsCmd())
cmd.AddCommand(NewBalancesCmd())
cmd.AddCommand(NewBalanceCmd())
cmd.AddCommand(NewStablecoinsCmd())

return cmd
}
29 changes: 29 additions & 0 deletions cmd/sim/evm/stablecoins.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package evm

import (
"github.com/spf13/cobra"
)

// NewStablecoinsCmd returns the `sim evm stablecoins` command.
func NewStablecoinsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "stablecoins <address>",
Short: "Get stablecoin balances for a wallet address",
Long: "Return stablecoin balances for the given wallet address across supported\n" +
"EVM chains, including USD valuations.\n\n" +
"Examples:\n" +
" dune sim evm stablecoins 0xd8da6bf26964af9d7eed9e03e53415d37aa96045\n" +
" dune sim evm stablecoins 0xd8da... --chain-ids 1,8453\n" +
" dune sim evm stablecoins 0xd8da... -o json",
Args: cobra.ExactArgs(1),
RunE: runStablecoins,
}

addBalanceFlags(cmd)

return cmd
}

func runStablecoins(cmd *cobra.Command, args []string) error {
return runBalancesEndpoint(cmd, args, "/v1/evm/balances/", "/stablecoins")
}
42 changes: 42 additions & 0 deletions cmd/sim/evm/stablecoins_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package evm_test

import (
"bytes"
"encoding/json"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestEvmStablecoins_Text(t *testing.T) {
key := simAPIKey(t)

root := newSimTestRoot()
var buf bytes.Buffer
root.SetOut(&buf)
root.SetArgs([]string{"sim", "--sim-api-key", key, "evm", "stablecoins", evmTestAddress, "--chain-ids", "1"})

require.NoError(t, root.Execute())

out := buf.String()
assert.Contains(t, out, "CHAIN")
assert.Contains(t, out, "SYMBOL")
assert.Contains(t, out, "VALUE_USD")
}

func TestEvmStablecoins_JSON(t *testing.T) {
key := simAPIKey(t)

root := newSimTestRoot()
var buf bytes.Buffer
root.SetOut(&buf)
root.SetArgs([]string{"sim", "--sim-api-key", key, "evm", "stablecoins", evmTestAddress, "--chain-ids", "1", "-o", "json"})

require.NoError(t, root.Execute())

var resp map[string]interface{}
require.NoError(t, json.Unmarshal(buf.Bytes(), &resp))
assert.Contains(t, resp, "wallet_address")
assert.Contains(t, resp, "balances")
}
Loading