Skip to content

Commit df65c19

Browse files
committed
Get balance
1 parent 88d2a96 commit df65c19

File tree

3 files changed

+159
-0
lines changed

3 files changed

+159
-0
lines changed

cmd/sim/evm/balance.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package evm
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/url"
7+
8+
"github.com/spf13/cobra"
9+
10+
"github.com/duneanalytics/cli/output"
11+
)
12+
13+
// NewBalanceCmd returns the `sim evm balance` command (single token).
14+
func NewBalanceCmd() *cobra.Command {
15+
cmd := &cobra.Command{
16+
Use: "balance <wallet_address>",
17+
Short: "Get balance for a single token",
18+
Long: "Return the balance of a single token for the given wallet address on one chain.\n" +
19+
"Use \"native\" as the --token value to query the native asset (e.g. ETH).\n\n" +
20+
"Examples:\n" +
21+
" dune sim evm balance 0xd8da... --token native --chain-ids 1\n" +
22+
" dune sim evm balance 0xd8da... --token 0xa0b8...eb48 --chain-ids 8453\n" +
23+
" dune sim evm balance 0xd8da... --token native --chain-ids 1 -o json",
24+
Args: cobra.ExactArgs(1),
25+
RunE: runBalance,
26+
}
27+
28+
cmd.Flags().String("token", "", "Token contract address or \"native\" (required)")
29+
cmd.Flags().String("chain-ids", "", "Chain ID (required)")
30+
_ = cmd.MarkFlagRequired("token")
31+
_ = cmd.MarkFlagRequired("chain-ids")
32+
output.AddFormatFlag(cmd, "text")
33+
34+
return cmd
35+
}
36+
37+
func runBalance(cmd *cobra.Command, args []string) error {
38+
client, err := requireSimClient(cmd)
39+
if err != nil {
40+
return err
41+
}
42+
43+
address := args[0]
44+
tokenAddress, _ := cmd.Flags().GetString("token")
45+
46+
params := url.Values{}
47+
if v, _ := cmd.Flags().GetString("chain-ids"); v != "" {
48+
params.Set("chain_ids", v)
49+
}
50+
51+
path := fmt.Sprintf("/v1/evm/balances/%s/token/%s", address, tokenAddress)
52+
data, err := client.Get(cmd.Context(), path, params)
53+
if err != nil {
54+
return err
55+
}
56+
57+
w := cmd.OutOrStdout()
58+
switch output.FormatFromCmd(cmd) {
59+
case output.FormatJSON:
60+
var raw json.RawMessage = data
61+
return output.PrintJSON(w, raw)
62+
default:
63+
var resp balancesResponse
64+
if err := json.Unmarshal(data, &resp); err != nil {
65+
return fmt.Errorf("parsing response: %w", err)
66+
}
67+
68+
if len(resp.Balances) == 0 {
69+
fmt.Fprintln(w, "No balance found.")
70+
return nil
71+
}
72+
73+
b := resp.Balances[0]
74+
fmt.Fprintf(w, "Chain: %s (ID: %d)\n", b.Chain, b.ChainID)
75+
fmt.Fprintf(w, "Token: %s\n", b.Address)
76+
fmt.Fprintf(w, "Symbol: %s\n", b.Symbol)
77+
if b.Name != "" {
78+
fmt.Fprintf(w, "Name: %s\n", b.Name)
79+
}
80+
fmt.Fprintf(w, "Decimals: %d\n", b.Decimals)
81+
fmt.Fprintf(w, "Amount: %s\n", formatAmount(b.Amount, b.Decimals))
82+
fmt.Fprintf(w, "Price USD: %s\n", formatUSD(b.PriceUSD))
83+
fmt.Fprintf(w, "Value USD: %s\n", formatUSD(b.ValueUSD))
84+
85+
return nil
86+
}
87+
}

cmd/sim/evm/balance_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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 TestEvmBalance_NativeText(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", "balance", evmTestAddress, "--token", "native", "--chain-ids", "1"})
19+
20+
require.NoError(t, root.Execute())
21+
22+
out := buf.String()
23+
assert.Contains(t, out, "Chain:")
24+
assert.Contains(t, out, "Symbol:")
25+
assert.Contains(t, out, "ETH")
26+
assert.Contains(t, out, "Amount:")
27+
assert.Contains(t, out, "Price USD:")
28+
assert.Contains(t, out, "Value USD:")
29+
}
30+
31+
func TestEvmBalance_NativeJSON(t *testing.T) {
32+
key := simAPIKey(t)
33+
34+
root := newSimTestRoot()
35+
var buf bytes.Buffer
36+
root.SetOut(&buf)
37+
root.SetArgs([]string{"sim", "--sim-api-key", key, "evm", "balance", evmTestAddress, "--token", "native", "--chain-ids", "1", "-o", "json"})
38+
39+
require.NoError(t, root.Execute())
40+
41+
var resp map[string]interface{}
42+
require.NoError(t, json.Unmarshal(buf.Bytes(), &resp))
43+
assert.Contains(t, resp, "wallet_address")
44+
assert.Contains(t, resp, "balances")
45+
46+
balances, ok := resp["balances"].([]interface{})
47+
require.True(t, ok)
48+
require.Len(t, balances, 1)
49+
50+
first, ok := balances[0].(map[string]interface{})
51+
require.True(t, ok)
52+
assert.Equal(t, "native", first["address"])
53+
}
54+
55+
func TestEvmBalance_MissingRequiredFlags(t *testing.T) {
56+
key := simAPIKey(t)
57+
58+
// Missing --token
59+
root := newSimTestRoot()
60+
root.SetArgs([]string{"sim", "--sim-api-key", key, "evm", "balance", evmTestAddress, "--chain-ids", "1"})
61+
err := root.Execute()
62+
assert.Error(t, err)
63+
assert.Contains(t, err.Error(), "token")
64+
65+
// Missing --chain-ids
66+
root2 := newSimTestRoot()
67+
root2.SetArgs([]string{"sim", "--sim-api-key", key, "evm", "balance", evmTestAddress, "--token", "native"})
68+
err2 := root2.Execute()
69+
assert.Error(t, err2)
70+
assert.Contains(t, err2.Error(), "chain-ids")
71+
}

cmd/sim/evm/evm.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func NewEvmCmd() *cobra.Command {
4040

4141
cmd.AddCommand(NewSupportedChainsCmd())
4242
cmd.AddCommand(NewBalancesCmd())
43+
cmd.AddCommand(NewBalanceCmd())
4344

4445
return cmd
4546
}

0 commit comments

Comments
 (0)