Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop NEPXXTokenInfo from the RPC client #2667

Merged
merged 6 commits into from
Aug 30, 2022
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
74 changes: 51 additions & 23 deletions cli/nep11_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,19 +167,35 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
"--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", wall,
"--address", nftOwnerAddr}
checkBalanceResult := func(t *testing.T, acc string, amount string) {
checkBalanceResult := func(t *testing.T, acc string, ids ...[]byte) {
e.checkNextLine(t, "^\\s*Account\\s+"+acc)
e.checkNextLine(t, "^\\s*HASHY:\\s+HASHY NFT \\("+h.StringLE()+"\\)")
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+amount+"$")

// Hashes can be ordered in any way, so make a regexp for them.
var tokstring = "("
for i, id := range ids {
if i > 0 {
tokstring += "|"
}
tokstring += hex.EncodeToString(id)
}
tokstring += ")"

for range ids {
e.checkNextLine(t, "^\\s*Token: "+tokstring+"\\s*$")
e.checkNextLine(t, "^\\s*Amount: 1\\s*$")
e.checkNextLine(t, "^\\s*Updated: [0-9]+\\s*$")
}
e.checkEOF(t)
}
// balance check: by symbol, token is not imported
e.RunWithError(t, append(cmdCheckBalance, "--token", "HASHY")...)
e.Run(t, append(cmdCheckBalance, "--token", "HASHY")...)
checkBalanceResult(t, nftOwnerAddr, tokenID)
// balance check: excessive parameters
e.RunWithError(t, append(cmdCheckBalance, "--token", h.StringLE(), "neo-go")...)
// balance check: by hash, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, nftOwnerAddr, "1")
checkBalanceResult(t, nftOwnerAddr, tokenID)

// import token
e.Run(t, "neo-go", "wallet", "nep11", "import",
Expand All @@ -189,14 +205,14 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {

// balance check: by symbol, ok
e.Run(t, append(cmdCheckBalance, "--token", "HASHY")...)
checkBalanceResult(t, nftOwnerAddr, "1")
checkBalanceResult(t, nftOwnerAddr, tokenID)

// balance check: all accounts
e.Run(t, "neo-go", "wallet", "nep11", "balance",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wall,
"--token", h.StringLE())
checkBalanceResult(t, nftOwnerAddr, "1")
checkBalanceResult(t, nftOwnerAddr, tokenID)

// remove token from wallet
e.In.WriteString("y\r")
Expand Down Expand Up @@ -276,7 +292,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {

// balance check: several tokens, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, nftOwnerAddr, "2")
checkBalanceResult(t, nftOwnerAddr, tokenID, tokenID1)

cmdTransfer := []string{
"neo-go", "wallet", "nep11", "transfer",
Expand Down Expand Up @@ -304,7 +320,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {

// check balance after transfer
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, nftOwnerAddr, "1") // tokenID1
checkBalanceResult(t, nftOwnerAddr, tokenID1)

// transfer: good, to NEP-11-Payable contract, with data
verifyH := deployVerifyContract(t, e)
Expand Down Expand Up @@ -341,7 +357,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {

// check balance after transfer
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, nftOwnerAddr, "0")
checkBalanceResult(t, nftOwnerAddr)
}

func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
Expand Down Expand Up @@ -406,31 +422,42 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
require.Equal(t, base64.StdEncoding.EncodeToString(object1ID.BytesBE()), props["objectID"])
e.checkEOF(t)

type idAmount struct {
id string
amount string
}

// check the balance
cmdCheckBalance := []string{"neo-go", "wallet", "nep11", "balance",
"--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", wall,
"--address", validatorAddr}
checkBalanceResult := func(t *testing.T, acc string, amount string, id []byte) {
checkBalanceResult := func(t *testing.T, acc string, objs ...idAmount) {
e.checkNextLine(t, "^\\s*Account\\s+"+acc)
if id == nil {
e.checkNextLine(t, "^\\s*NFSO:\\s+NeoFS Object NFT \\("+h.StringLE()+"\\)")
} else {
e.checkNextLine(t, "^\\s*NFSO:\\s+NeoFS Object NFT \\("+h.StringLE()+", "+hex.EncodeToString(id)+"\\)")
e.checkNextLine(t, "^\\s*NFSO:\\s+NeoFS Object NFT \\("+h.StringLE()+"\\)")

for _, o := range objs {
e.checkNextLine(t, "^\\s*Token: "+o.id+"\\s*$")
e.checkNextLine(t, "^\\s*Amount: "+o.amount+"\\s*$")
e.checkNextLine(t, "^\\s*Updated: [0-9]+\\s*$")
}
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+amount+"$")
e.checkEOF(t)
}
tokz := []idAmount{
{hex.EncodeToString(token1ID), "1"},
{hex.EncodeToString(token2ID), "1"},
}
// balance check: by symbol, token is not imported
e.RunWithError(t, append(cmdCheckBalance, "--token", "NFSO")...)
e.Run(t, append(cmdCheckBalance, "--token", "NFSO")...)
checkBalanceResult(t, validatorAddr, tokz...)

// overall NFSO balance check: by hash, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, validatorAddr, "2", nil)
checkBalanceResult(t, validatorAddr, tokz...)

// particular NFSO balance check: by hash, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE(), "--id", hex.EncodeToString(token2ID))...)
checkBalanceResult(t, validatorAddr, "1", token2ID)
checkBalanceResult(t, validatorAddr, tokz[1])

// import token
e.Run(t, "neo-go", "wallet", "nep11", "import",
Expand All @@ -440,11 +467,11 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {

// overall balance check: by symbol, ok
e.Run(t, append(cmdCheckBalance, "--token", "NFSO")...)
checkBalanceResult(t, validatorAddr, "2", nil)
checkBalanceResult(t, validatorAddr, tokz...)

// particular balance check: by symbol, ok
e.Run(t, append(cmdCheckBalance, "--token", "NFSO", "--id", hex.EncodeToString(token1ID))...)
checkBalanceResult(t, validatorAddr, "1", token1ID)
checkBalanceResult(t, validatorAddr, tokz[0])

// remove token from wallet
e.In.WriteString("y\r")
Expand Down Expand Up @@ -531,7 +558,7 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {

// balance check: several tokens, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, validatorAddr, "2", nil)
checkBalanceResult(t, validatorAddr, tokz...)

cmdTransfer := []string{
"neo-go", "wallet", "nep11", "transfer",
Expand Down Expand Up @@ -559,7 +586,7 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {

// check balance after transfer
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, validatorAddr, "1", nil) // only token2ID expected to be on the balance
checkBalanceResult(t, validatorAddr, tokz[1]) // only token2ID expected to be on the balance

// transfer: good, 1/4 of the balance, to NEP-11-Payable contract, with data
verifyH := deployVerifyContract(t, e)
Expand Down Expand Up @@ -597,7 +624,8 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {

// check balance after transfer
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, validatorAddr, "0.75", nil)
tokz[1].amount = "0.75"
checkBalanceResult(t, validatorAddr, tokz[1])
}

func deployNFSContract(t *testing.T, e *executor) util.Uint160 {
Expand Down
1 change: 1 addition & 0 deletions cli/nep17_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ func TestNEP17Balance(t *testing.T) {
t.Run("Bad token", func(t *testing.T) {
e.Run(t, append(cmd, "--token", "kek")...)
e.checkNextLine(t, "^\\s*Account\\s+"+validatorAddr)
e.checkNextLine(t, `^\s*Can't find data for "kek" token\s*`)
e.checkEOF(t)
})
t.Run("Bad wallet", func(t *testing.T) {
Expand Down
139 changes: 54 additions & 85 deletions cli/wallet/nep11.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ import (
"encoding/hex"
"errors"
"fmt"
"math/big"
"strconv"

"github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli"
Expand Down Expand Up @@ -48,9 +49,24 @@ 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>]",
Action: getNEP11Balance,
Flags: balanceFlags,
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
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
particular NFT ID (hex-encoded) to display (which is mostly useful for
divisible NFTs). Tokens can be specified by hash, address, name or symbol.
Hashes and addresses always work (as long as they belong to a correct NEP-11
contract), while names or symbols are matched against the token data
stored in the wallet (see import command) or balance data returned from the
server. If the token is not specified directly (with hash/address) and is
not found in the wallet then depending on the balances data from the server
this command can print no data at all or print multiple tokens for one
account (if they use the same names/symbols).
`,
Action: getNEP11Balance,
Flags: balanceFlags,
},
{
Name: "import",
Expand Down Expand Up @@ -162,95 +178,48 @@ func removeNEP11Token(ctx *cli.Context) error {
}

func getNEP11Balance(ctx *cli.Context) error {
var accounts []*wallet.Account

if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}

wall, _, err := readWallet(ctx)
if err != nil {
return cli.NewExitError(fmt.Errorf("bad wallet: %w", err), 1)
}

addrFlag := ctx.Generic("address").(*flags.Address)
if addrFlag.IsSet {
addrHash := addrFlag.Uint160()
acc := wall.GetAccount(addrHash)
if acc == nil {
return cli.NewExitError(fmt.Errorf("can't find account for the address: %s", address.Uint160ToString(addrHash)), 1)
}
accounts = append(accounts, acc)
} else {
if len(wall.Accounts) == 0 {
return cli.NewExitError(errors.New("no accounts in the wallet"), 1)
}
accounts = wall.Accounts
}

gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()

c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return cli.NewExitError(err, 1)
}

name := ctx.String("token")
if name == "" {
return cli.NewExitError("token hash or name should be specified", 1)
}
token, err := getMatchingToken(ctx, wall, name, manifest.NEP11StandardName)
if err != nil {
tokenHash, err := flags.ParseAddress(name)
return getNEPBalance(ctx, manifest.NEP11StandardName, func(ctx *cli.Context, c *rpcclient.Client, addrHash util.Uint160, name string, token *wallet.Token, nftID string) error {
balances, err := c.GetNEP11Balances(addrHash)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't fetch matching token from RPC-node: %w", err), 1)
return err
}
token, err = c.NEP11TokenInfo(tokenHash)
if err != nil {
return cli.NewExitError(err.Error(), 1)
var tokenFound bool
for i := range balances.Balances {
curToken := tokenFromNEP11Balance(&balances.Balances[i])
if tokenMatch(curToken, token, name) {
printNFTBalance(ctx, balances.Balances[i], nftID)
tokenFound = true
}
}
}
// Always initialize divisible token to be able to use both balanceOf methods.
n11 := nep11.NewDivisibleReader(invoker.New(c, nil), token.Hash)

tokenID := ctx.String("id")
tokenIDBytes, err := hex.DecodeString(tokenID)
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid tokenID bytes: %w", err), 1)
}
for k, acc := range accounts {
addrHash, err := address.StringToUint160(acc.Address)
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid account address: %w", err), 1)
}

if k != 0 {
fmt.Fprintln(ctx.App.Writer)
if name == "" || tokenFound {
return nil
}
fmt.Fprintf(ctx.App.Writer, "Account %s\n", acc.Address)

var amount *big.Int
if len(tokenIDBytes) == 0 {
amount, err = n11.BalanceOf(addrHash)
if token != nil {
// We have an exact token, but there is no balance data for it -> print without NFTs.
printNFTBalance(ctx, result.NEP11AssetBalance{
Asset: token.Hash,
Decimals: int(token.Decimals),
Name: token.Name,
Symbol: token.Symbol,
}, "")
} else {
amount, err = n11.BalanceOfD(addrHash, tokenIDBytes)
}
if err != nil {
continue
// We have no data for this token at all, maybe it's not even correct -> complain.
fmt.Fprintf(ctx.App.Writer, "Can't find data for %q token\n", name)
}
amountStr := fixedn.ToString(amount, int(token.Decimals))
return nil
})
}

format := "%s: %s (%s)\n"
formatArgs := []interface{}{token.Symbol, token.Name, token.Hash.StringLE()}
if len(tokenIDBytes) != 0 {
format = "%s: %s (%s, %s)\n"
formatArgs = append(formatArgs, tokenID)
func printNFTBalance(ctx *cli.Context, balance result.NEP11AssetBalance, nftID string) {
fmt.Fprintf(ctx.App.Writer, "%s: %s (%s)\n", balance.Symbol, balance.Name, balance.Asset.StringLE())
for _, tok := range balance.Tokens {
if len(nftID) > 0 && nftID != tok.ID {
continue
}
fmt.Fprintf(ctx.App.Writer, format, formatArgs...)
fmt.Fprintf(ctx.App.Writer, "\tAmount : %s\n", amountStr)
fmt.Fprintf(ctx.App.Writer, "\tToken: %s\n", tok.ID)
fmt.Fprintf(ctx.App.Writer, "\t\tAmount: %s\n", decimalAmount(tok.Amount, balance.Decimals))
fmt.Fprintf(ctx.App.Writer, "\t\tUpdated: %d\n", tok.LastUpdated)
}
return nil
}

func transferNEP11(ctx *cli.Context) error {
Expand Down
Loading