Skip to content

Commit

Permalink
cli: make nep1X balance commands work without a wallet, fix #3275
Browse files Browse the repository at this point in the history
Checking the balance should be easy without any wallets involved, the data
is available anyway.

Adding the command to "query nep1X" is not so trivial, so let's have it this
way for now..

Signed-off-by: Roman Khimov <[email protected]>
  • Loading branch information
roman-khimov committed Nov 2, 2024
1 parent 95098d4 commit 2be0364
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 45 deletions.
72 changes: 45 additions & 27 deletions cli/nep_test/nep17_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,57 @@ func TestNEP17Balance(t *testing.T) {
e.Run(t, args...)
e.CheckTxPersisted(t)

var checkAcc1NEO = func(t *testing.T, e *testcli.Executor, line string) {
if line == "" {
line = e.GetNextLine(t)
}
balance, index := e.Chain.GetGoverningTokenBalance(testcli.TestWalletMultiAccount1Hash)
e.CheckLine(t, line, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+balance.String()+"$")
e.CheckNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
}

var checkAcc1GAS = func(t *testing.T, e *testcli.Executor, line string) {
if line == "" {
line = e.GetNextLine(t)
}
e.CheckLine(t, line, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
balance := e.Chain.GetUtilityTokenBalance(testcli.TestWalletMultiAccount1Hash)
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(balance.Int64()).String()+"$")
e.CheckNextLine(t, "^\\s*Updated:")
}
var checkAcc1Assets = func(t *testing.T, e *testcli.Executor) {
e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount1)
// The order of assets is undefined.
for range 2 {
line := e.GetNextLine(t)
if strings.Contains(line, "GAS") {
checkAcc1GAS(t, e, line)
} else {
checkAcc1NEO(t, e, line)
}
}
}

cmdbalance := []string{"neo-go", "wallet", "nep17", "balance"}
cmdbase := append(cmdbalance,
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.TestWalletMultiPath,
)
cmdbase := append(cmdbalance, "--rpc-endpoint", "http://"+e.RPC.Addresses()[0])
t.Run("no wallet or address", func(t *testing.T) {
e.RunWithError(t, cmdbase...)
})
t.Run("address-based", func(t *testing.T) {
e.Run(t, append(cmdbase, "--address", testcli.TestWalletMultiAccount1)...)
checkAcc1Assets(t, e)
e.CheckEOF(t)
})
cmdbase = append(cmdbase, "--wallet", testcli.TestWalletMultiPath)
cmd := append(cmdbase, "--address", testcli.TestWalletMultiAccount1)
t.Run("excessive parameters", func(t *testing.T) {
e.RunWithError(t, append(cmd, "--token", "NEO", "gas")...)
})
t.Run("NEO", func(t *testing.T) {
b, index := e.Chain.GetGoverningTokenBalance(testcli.TestWalletMultiAccount1Hash)
checkResult := func(t *testing.T) {
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1)
e.CheckNextLine(t, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+b.String()+"$")
e.CheckNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
checkAcc1NEO(t, e, "")
e.CheckEOF(t)
}
t.Run("Alias", func(t *testing.T) {
Expand All @@ -64,9 +99,7 @@ func TestNEP17Balance(t *testing.T) {
t.Run("GAS", func(t *testing.T) {
e.Run(t, append(cmd, "--token", "GAS")...)
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1)
e.CheckNextLine(t, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
b := e.Chain.GetUtilityTokenBalance(testcli.TestWalletMultiAccount1Hash)
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(b.Int64()).String()+"$")
checkAcc1GAS(t, e, "")
})
t.Run("zero balance of known token", func(t *testing.T) {
e.Run(t, append(cmdbase, []string{"--token", "NEO", "--address", testcli.TestWalletMultiAccount2}...)...)
Expand All @@ -79,22 +112,7 @@ func TestNEP17Balance(t *testing.T) {
t.Run("all accounts", func(t *testing.T) {
e.Run(t, cmdbase...)

e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount1)
// The order of assets is undefined.
for range 2 {
line := e.GetNextLine(t)
if strings.Contains(line, "GAS") {
e.CheckLine(t, line, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
balance := e.Chain.GetUtilityTokenBalance(testcli.TestWalletMultiAccount1Hash)
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(balance.Int64()).String()+"$")
e.CheckNextLine(t, "^\\s*Updated:")
} else {
balance, index := e.Chain.GetGoverningTokenBalance(testcli.TestWalletMultiAccount1Hash)
e.CheckLine(t, line, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+balance.String()+"$")
e.CheckNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
}
}
checkAcc1Assets(t, e)
e.CheckNextLine(t, "^\\s*$")

e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount2)
Expand Down
7 changes: 4 additions & 3 deletions cli/wallet/nep11.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,10 @@ func newNEP11Commands() []*cli.Command {
{
Name: "balance",
Usage: "Get address balance",
UsageText: "balance -w wallet [--wallet-config path] --rpc-endpoint <node> [--timeout <time>] [--address <address>] [--token <hash-or-name>] [--id <token-id>]",
Description: `Prints NEP-11 balances for address and assets/IDs specified. By default (no
address or token parameter) all tokens (NFT contracts) for all accounts in
UsageText: "balance [-w wallet] [--wallet-config path] --rpc-endpoint <node> [--timeout <time>] [--address <address>] [--token <hash-or-name>] [--id <token-id>]",
Description: `Prints NEP-17 balances for address and assets/IDs specified. One of wallet
or address must be specified, passing both is valid too. If a wallet is
given without an address all tokens (NFT contracts) for all accounts in
the specified wallet are listed with all tokens (actual NFTs) insied. A
single account can be chosen with the address option and/or a single NFT
contract can be selected with the token option. Further, you can specify a
Expand Down
44 changes: 29 additions & 15 deletions cli/wallet/nep17.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,11 @@ func newNEP17Commands() []*cli.Command {
{
Name: "balance",
Usage: "Get address balance",
UsageText: "balance -w wallet [--wallet-config path] --rpc-endpoint <node> [--timeout <time>] [--address <address>] [--token <hash-or-name>]",
Description: `Prints NEP-17 balances for address and tokens specified. By default (no
address or token parameter) all tokens for all accounts in the specified wallet
are listed. A single account can be chosen with the address option and/or a
UsageText: "balance [-w wallet] [--wallet-config path] --rpc-endpoint <node> [--timeout <time>] [--address <address>] [--token <hash-or-name>]",
Description: `Prints NEP-17 balances for address and tokens specified. One of wallet
or address must be specified, passing both is valid too. If a wallet is
given without an address all tokens for all accounts in this wallet are
listed. A single account can be chosen with the address option and/or a
single token can be selected with the token option. Tokens can be specified
by hash, address, name or symbol. Hashes and addresses always work (as long
as they belong to a correct NEP-17 contract), while names or symbols (if
Expand Down Expand Up @@ -217,30 +218,40 @@ func getNEP17Balance(ctx *cli.Context) error {
}

func getNEPBalance(ctx *cli.Context, standard string, accHandler func(*cli.Context, *rpcclient.Client, util.Uint160, string, *wallet.Token, string) error) error {
var accounts []*wallet.Account
var addresses []util.Uint160

if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
wall, _, err := readWallet(ctx)
if err != nil {
return cli.Exit(fmt.Errorf("bad wallet: %w", err), 1)
if !errors.Is(err, errNoPath) {
return cli.Exit(fmt.Errorf("bad wallet: %w", err), 1)
}
} else {
defer wall.Close()
}
defer wall.Close()

addrFlag := ctx.Generic("address").(*flags.Address)
if addrFlag.IsSet {
addrHash := addrFlag.Uint160()
acc := wall.GetAccount(addrHash)
if acc == nil {
return cli.Exit(fmt.Errorf("can't find account for the address: %s", address.Uint160ToString(addrHash)), 1)
if wall != nil {
acc := wall.GetAccount(addrHash)
if acc == nil {
return cli.Exit(fmt.Errorf("can't find account for the address: %s", address.Uint160ToString(addrHash)), 1)
}

Check warning on line 242 in cli/wallet/nep17.go

View check run for this annotation

Codecov / codecov/patch

cli/wallet/nep17.go#L241-L242

Added lines #L241 - L242 were not covered by tests
}
accounts = append(accounts, acc)
addresses = append(addresses, addrHash)
} else {
if wall == nil {
return cli.Exit(errors.New("neither wallet nor address specified"), 1)
}
if len(wall.Accounts) == 0 {
return cli.Exit(errors.New("no accounts in the wallet"), 1)
}
accounts = wall.Accounts
for _, acc := range wall.Accounts {
addresses = append(addresses, acc.ScriptHash())
}
}

gctx, cancel := options.GetTimeoutContext(ctx)
Expand Down Expand Up @@ -290,13 +301,13 @@ func getNEPBalance(ctx *cli.Context, standard string, accHandler func(*cli.Conte
}
}
}
for k, acc := range accounts {
for k, addr := range addresses {
if k != 0 {
fmt.Fprintln(ctx.App.Writer)
}
fmt.Fprintf(ctx.App.Writer, "Account %s\n", acc.Address)
fmt.Fprintf(ctx.App.Writer, "Account %s\n", address.Uint160ToString(addr))

err = accHandler(ctx, c, acc.ScriptHash(), name, token, tokenID)
err = accHandler(ctx, c, addr, name, token, tokenID)
if err != nil {
return cli.Exit(err, 1)
}
Expand All @@ -321,6 +332,9 @@ func printAssetBalance(ctx *cli.Context, balance result.NEP17Balance) {
}

func getMatchingToken(ctx *cli.Context, w *wallet.Wallet, name string, standard string) (*wallet.Token, error) {
if w == nil {
return getMatchingTokenAux(ctx, nil, 0, name, standard)
}

Check warning on line 337 in cli/wallet/nep17.go

View check run for this annotation

Codecov / codecov/patch

cli/wallet/nep17.go#L336-L337

Added lines #L336 - L337 were not covered by tests
return getMatchingTokenAux(ctx, func(i int) *wallet.Token {
return w.Extra.Tokens[i]
}, len(w.Extra.Tokens), name, standard)
Expand Down

0 comments on commit 2be0364

Please sign in to comment.