mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-23 03:41:34 +00:00
Merge pull request #3659 from nspcc-dev/make-balance-work-without-wallet
cli: make nep1X balance commands work without a wallet, fix #3275
This commit is contained in:
commit
cee296eb92
3 changed files with 127 additions and 75 deletions
|
@ -3,6 +3,7 @@ package nep_test
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -34,39 +35,99 @@ func TestNEP17Balance(t *testing.T) {
|
||||||
e.Run(t, args...)
|
e.Run(t, args...)
|
||||||
e.CheckTxPersisted(t)
|
e.CheckTxPersisted(t)
|
||||||
|
|
||||||
cmdbalance := []string{"neo-go", "wallet", "nep17", "balance"}
|
var checkAcc1NEO = func(t *testing.T, e *testcli.Executor, line string) {
|
||||||
cmdbase := append(cmdbalance,
|
if line == "" {
|
||||||
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
line = e.GetNextLine(t)
|
||||||
"--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))
|
|
||||||
e.CheckEOF(t)
|
|
||||||
}
|
}
|
||||||
t.Run("Alias", func(t *testing.T) {
|
balance, index := e.Chain.GetGoverningTokenBalance(testcli.TestWalletMultiAccount1Hash)
|
||||||
e.Run(t, append(cmd, "--token", "NEO")...)
|
e.CheckLine(t, line, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
|
||||||
checkResult(t)
|
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+balance.String()+"$")
|
||||||
})
|
e.CheckNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
|
||||||
t.Run("Hash", func(t *testing.T) {
|
}
|
||||||
e.Run(t, append(cmd, "--token", e.Chain.GoverningTokenHash().StringLE())...)
|
var checkAcc1GAS = func(t *testing.T, e *testcli.Executor, line string) {
|
||||||
checkResult(t)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
cmdbase = []string{"neo-go", "wallet", "nep17", "balance", "--rpc-endpoint", "http://" + e.RPC.Addresses()[0]}
|
||||||
|
addrparams = []string{"--address", testcli.TestWalletMultiAccount1}
|
||||||
|
walletparams = []string{"--wallet", testcli.TestWalletMultiPath}
|
||||||
|
)
|
||||||
|
t.Run("Bad wallet", func(t *testing.T) {
|
||||||
|
e.RunWithError(t, append(cmdbase, "--wallet", "/dev/null")...)
|
||||||
})
|
})
|
||||||
t.Run("GAS", func(t *testing.T) {
|
t.Run("empty wallet", func(t *testing.T) {
|
||||||
e.Run(t, append(cmd, "--token", "GAS")...)
|
tmpDir := t.TempDir()
|
||||||
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1)
|
walletPath := filepath.Join(tmpDir, "emptywallet.json")
|
||||||
e.CheckNextLine(t, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
|
require.NoError(t, os.WriteFile(walletPath, []byte("{}"), 0o644))
|
||||||
b := e.Chain.GetUtilityTokenBalance(testcli.TestWalletMultiAccount1Hash)
|
e.RunWithError(t, append(cmdbase, "--wallet", walletPath)...)
|
||||||
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(b.Int64()).String()+"$")
|
})
|
||||||
|
t.Run("no wallet or address", func(t *testing.T) {
|
||||||
|
e.RunWithError(t, cmdbase...)
|
||||||
|
})
|
||||||
|
for name, params := range map[string][]string{
|
||||||
|
"address only": addrparams,
|
||||||
|
"address with wallet": slices.Concat(walletparams, addrparams),
|
||||||
|
} {
|
||||||
|
var cmd = append(cmdbase, params...)
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Run("all tokens", func(t *testing.T) {
|
||||||
|
e.Run(t, cmd...)
|
||||||
|
checkAcc1Assets(t, e)
|
||||||
|
e.CheckEOF(t)
|
||||||
|
})
|
||||||
|
t.Run("excessive parameters", func(t *testing.T) {
|
||||||
|
e.RunWithError(t, append(cmd, "--token", "NEO", "gas")...)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("NEO", func(t *testing.T) {
|
||||||
|
checkResult := func(t *testing.T) {
|
||||||
|
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1)
|
||||||
|
checkAcc1NEO(t, e, "")
|
||||||
|
e.CheckEOF(t)
|
||||||
|
}
|
||||||
|
t.Run("Alias", func(t *testing.T) {
|
||||||
|
e.Run(t, append(cmd, "--token", "NEO")...)
|
||||||
|
checkResult(t)
|
||||||
|
})
|
||||||
|
t.Run("Hash", func(t *testing.T) {
|
||||||
|
e.Run(t, append(cmd, "--token", e.Chain.GoverningTokenHash().StringLE())...)
|
||||||
|
checkResult(t)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("GAS", func(t *testing.T) {
|
||||||
|
e.Run(t, append(cmd, "--token", "GAS")...)
|
||||||
|
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1)
|
||||||
|
checkAcc1GAS(t, e, "")
|
||||||
|
})
|
||||||
|
t.Run("Bad token", func(t *testing.T) {
|
||||||
|
e.Run(t, append(cmd, "--token", "kek")...)
|
||||||
|
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1)
|
||||||
|
e.CheckNextLine(t, `^\s*Can't find data for "kek" token\s*`)
|
||||||
|
e.CheckEOF(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
t.Run("inexistent wallet account", func(t *testing.T) {
|
||||||
|
var cmd = append(cmdbase, walletparams...)
|
||||||
|
e.RunWithError(t, append(cmd, "--address", "NSPCCpw8YmgNDYWiBfXJHRfz38NDjv6WW3")...)
|
||||||
})
|
})
|
||||||
t.Run("zero balance of known token", func(t *testing.T) {
|
t.Run("zero balance of known token", func(t *testing.T) {
|
||||||
e.Run(t, append(cmdbase, []string{"--token", "NEO", "--address", testcli.TestWalletMultiAccount2}...)...)
|
e.Run(t, append(cmdbase, []string{"--token", "NEO", "--address", testcli.TestWalletMultiAccount2}...)...)
|
||||||
|
@ -77,24 +138,9 @@ func TestNEP17Balance(t *testing.T) {
|
||||||
e.CheckEOF(t)
|
e.CheckEOF(t)
|
||||||
})
|
})
|
||||||
t.Run("all accounts", func(t *testing.T) {
|
t.Run("all accounts", func(t *testing.T) {
|
||||||
e.Run(t, cmdbase...)
|
e.Run(t, append(cmdbase, walletparams...)...)
|
||||||
|
|
||||||
e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount1)
|
checkAcc1Assets(t, e)
|
||||||
// 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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
e.CheckNextLine(t, "^\\s*$")
|
e.CheckNextLine(t, "^\\s*$")
|
||||||
|
|
||||||
e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount2)
|
e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount2)
|
||||||
|
@ -107,15 +153,6 @@ func TestNEP17Balance(t *testing.T) {
|
||||||
e.CheckNextLine(t, "^\\s*Updated:")
|
e.CheckNextLine(t, "^\\s*Updated:")
|
||||||
e.CheckEOF(t)
|
e.CheckEOF(t)
|
||||||
})
|
})
|
||||||
t.Run("Bad token", func(t *testing.T) {
|
|
||||||
e.Run(t, append(cmd, "--token", "kek")...)
|
|
||||||
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1)
|
|
||||||
e.CheckNextLine(t, `^\s*Can't find data for "kek" token\s*`)
|
|
||||||
e.CheckEOF(t)
|
|
||||||
})
|
|
||||||
t.Run("Bad wallet", func(t *testing.T) {
|
|
||||||
e.RunWithError(t, append(cmdbalance, "--wallet", "/dev/null", "-r", "test")...)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNEP17Transfer(t *testing.T) {
|
func TestNEP17Transfer(t *testing.T) {
|
||||||
|
|
|
@ -51,9 +51,10 @@ func newNEP11Commands() []*cli.Command {
|
||||||
{
|
{
|
||||||
Name: "balance",
|
Name: "balance",
|
||||||
Usage: "Get address 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>]",
|
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
|
Description: `Prints NEP-11 balances for address and assets/IDs specified. One of wallet
|
||||||
address or token parameter) all tokens (NFT contracts) for all accounts in
|
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
|
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
|
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
|
contract can be selected with the token option. Further, you can specify a
|
||||||
|
|
|
@ -101,10 +101,11 @@ func newNEP17Commands() []*cli.Command {
|
||||||
{
|
{
|
||||||
Name: "balance",
|
Name: "balance",
|
||||||
Usage: "Get address balance",
|
Usage: "Get address balance",
|
||||||
UsageText: "balance -w wallet [--wallet-config path] --rpc-endpoint <node> [--timeout <time>] [--address <address>] [--token <hash-or-name>]",
|
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
|
Description: `Prints NEP-17 balances for address and tokens specified. One of wallet
|
||||||
address or token parameter) all tokens for all accounts in the specified wallet
|
or address must be specified, passing both is valid too. If a wallet is
|
||||||
are listed. A single account can be chosen with the address option and/or a
|
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
|
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
|
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
|
as they belong to a correct NEP-17 contract), while names or symbols (if
|
||||||
|
@ -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 {
|
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 {
|
if err := cmdargs.EnsureNone(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
wall, _, err := readWallet(ctx)
|
wall, _, err := readWallet(ctx)
|
||||||
if err != nil {
|
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)
|
addrFlag := ctx.Generic("address").(*flags.Address)
|
||||||
if addrFlag.IsSet {
|
if addrFlag.IsSet {
|
||||||
addrHash := addrFlag.Uint160()
|
addrHash := addrFlag.Uint160()
|
||||||
acc := wall.GetAccount(addrHash)
|
if wall != nil {
|
||||||
if acc == nil {
|
acc := wall.GetAccount(addrHash)
|
||||||
return cli.Exit(fmt.Errorf("can't find account for the address: %s", address.Uint160ToString(addrHash)), 1)
|
if acc == nil {
|
||||||
|
return cli.Exit(fmt.Errorf("can't find account for the address: %s", address.Uint160ToString(addrHash)), 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
accounts = append(accounts, acc)
|
addresses = append(addresses, addrHash)
|
||||||
} else {
|
} else {
|
||||||
|
if wall == nil {
|
||||||
|
return cli.Exit(errors.New("neither wallet nor address specified"), 1)
|
||||||
|
}
|
||||||
if len(wall.Accounts) == 0 {
|
if len(wall.Accounts) == 0 {
|
||||||
return cli.Exit(errors.New("no accounts in the wallet"), 1)
|
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)
|
gctx, cancel := options.GetTimeoutContext(ctx)
|
||||||
|
@ -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 {
|
if k != 0 {
|
||||||
fmt.Fprintln(ctx.App.Writer)
|
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 {
|
if err != nil {
|
||||||
return cli.Exit(err, 1)
|
return cli.Exit(err, 1)
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
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)
|
||||||
|
}
|
||||||
return getMatchingTokenAux(ctx, func(i int) *wallet.Token {
|
return getMatchingTokenAux(ctx, func(i int) *wallet.Token {
|
||||||
return w.Extra.Tokens[i]
|
return w.Extra.Tokens[i]
|
||||||
}, len(w.Extra.Tokens), name, standard)
|
}, len(w.Extra.Tokens), name, standard)
|
||||||
|
|
Loading…
Reference in a new issue