neptoken: add Info to replace old NEPXXTokenInfo methods

I'm still not sure it's good to have this exposed from neptoken at all, but
let's try it this way.
This commit is contained in:
Roman Khimov 2022-08-26 22:56:18 +03:00
parent a3f32bf306
commit ed6ed61712
7 changed files with 222 additions and 17 deletions

View file

@ -206,7 +206,7 @@ func getNEP11Balance(ctx *cli.Context) error {
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't fetch matching token from RPC-node: %w", err), 1) return cli.NewExitError(fmt.Errorf("can't fetch matching token from RPC-node: %w", err), 1)
} }
token, err = c.NEP11TokenInfo(tokenHash) token, err = getTokenWithStandard(c, tokenHash, manifest.NEP11StandardName)
if err != nil { if err != nil {
return cli.NewExitError(err.Error(), 1) return cli.NewExitError(err.Error(), 1)
} }

View file

@ -24,6 +24,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neo" "github.com/nspcc-dev/neo-go/pkg/rpcclient/neo"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11" "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17" "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neptoken"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -214,7 +215,7 @@ func getNEP17Balance(ctx *cli.Context) error {
asset := balances.Balances[i].Asset asset := balances.Balances[i].Asset
token, err := getMatchingToken(ctx, wall, asset.StringLE(), manifest.NEP17StandardName) token, err := getMatchingToken(ctx, wall, asset.StringLE(), manifest.NEP17StandardName)
if err != nil { if err != nil {
token, err = c.NEP17TokenInfo(asset) token, err = getTokenWithStandard(c, asset, manifest.NEP17StandardName)
} }
if err == nil { if err == nil {
if name != "" && !(token.Name == name || token.Symbol == name || token.Address() == name || token.Hash.StringLE() == name) { if name != "" && !(token.Name == name || token.Symbol == name || token.Address() == name || token.Hash.StringLE() == name) {
@ -268,7 +269,7 @@ func getNEP17Balance(ctx *cli.Context) error {
} }
} }
} }
token, err = c.NEP17TokenInfo(h) token, err = getTokenWithStandard(c, h, manifest.NEP17StandardName)
if err != nil { if err != nil {
continue continue
} }
@ -309,7 +310,7 @@ func getMatchingTokenRPC(ctx *cli.Context, c *rpcclient.Client, addr util.Uint16
return nil, err return nil, err
} }
get := func(i int) *wallet.Token { get := func(i int) *wallet.Token {
t, _ := c.NEP17TokenInfo(bs.Balances[i].Asset) t, _ := getTokenWithStandard(c, bs.Balances[i].Asset, standard)
return t return t
} }
return getMatchingTokenAux(ctx, get, len(bs.Balances), name, standard) return getMatchingTokenAux(ctx, get, len(bs.Balances), name, standard)
@ -319,7 +320,7 @@ func getMatchingTokenRPC(ctx *cli.Context, c *rpcclient.Client, addr util.Uint16
return nil, fmt.Errorf("valid token adress or hash in LE should be specified for %s RPC-node request: %s", standard, err.Error()) return nil, fmt.Errorf("valid token adress or hash in LE should be specified for %s RPC-node request: %s", standard, err.Error())
} }
get := func(i int) *wallet.Token { get := func(i int) *wallet.Token {
t, _ := c.NEP11TokenInfo(tokenHash) t, _ := getTokenWithStandard(c, tokenHash, standard)
return t return t
} }
return getMatchingTokenAux(ctx, get, 1, name, standard) return getMatchingTokenAux(ctx, get, 1, name, standard)
@ -383,15 +384,7 @@ func importNEPToken(ctx *cli.Context, standard string) error {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
var tok *wallet.Token tok, err := getTokenWithStandard(c, tokenHash, standard)
switch standard {
case manifest.NEP17StandardName:
tok, err = c.NEP17TokenInfo(tokenHash)
case manifest.NEP11StandardName:
tok, err = c.NEP11TokenInfo(tokenHash)
default:
return cli.NewExitError(fmt.Sprintf("unsupported token standard: %s", standard), 1)
}
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't receive token info: %w", err), 1) return cli.NewExitError(fmt.Errorf("can't receive token info: %w", err), 1)
} }
@ -404,6 +397,17 @@ func importNEPToken(ctx *cli.Context, standard string) error {
return nil return nil
} }
func getTokenWithStandard(c *rpcclient.Client, hash util.Uint160, std string) (*wallet.Token, error) {
token, err := neptoken.Info(c, hash)
if err != nil {
return nil, err
}
if token.Standard != std {
return nil, fmt.Errorf("%s is not a %s token", hash.StringLE(), std)
}
return token, err
}
func printTokenInfo(ctx *cli.Context, tok *wallet.Token) { func printTokenInfo(ctx *cli.Context, tok *wallet.Token) {
w := ctx.App.Writer w := ctx.App.Writer
fmt.Fprintf(w, "Name:\t%s\n", tok.Name) fmt.Fprintf(w, "Name:\t%s\n", tok.Name)

View file

@ -49,6 +49,9 @@ func (c *Client) NEP11BalanceOf(tokenHash, owner util.Uint160) (int64, error) {
} }
// NEP11TokenInfo returns full NEP-11 token info. // NEP11TokenInfo returns full NEP-11 token info.
//
// Deprecated: please use Info method from the neptoken subpackage. This method
// will be removed in future versions.
func (c *Client) NEP11TokenInfo(tokenHash util.Uint160) (*wallet.Token, error) { func (c *Client) NEP11TokenInfo(tokenHash util.Uint160) (*wallet.Token, error) {
return c.nepTokenInfo(tokenHash, manifest.NEP11StandardName) return c.nepTokenInfo(tokenHash, manifest.NEP11StandardName)
} }

View file

@ -61,6 +61,9 @@ func (c *Client) NEP17BalanceOf(tokenHash, acc util.Uint160) (int64, error) {
} }
// NEP17TokenInfo returns full NEP-17 token info. // NEP17TokenInfo returns full NEP-17 token info.
//
// Deprecated: please use Info method from the neptoken subpackage. This method
// will be removed in future versions.
func (c *Client) NEP17TokenInfo(tokenHash util.Uint160) (*wallet.Token, error) { func (c *Client) NEP17TokenInfo(tokenHash util.Uint160) (*wallet.Token, error) {
return c.nepTokenInfo(tokenHash, manifest.NEP17StandardName) return c.nepTokenInfo(tokenHash, manifest.NEP17StandardName)
} }

View file

@ -0,0 +1,47 @@
package neptoken
import (
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
)
// InfoClient is a set of RPC methods required to get all of the NEP-11/NEP-17
// token data.
type InfoClient interface {
invoker.RPCInvoke
GetContractStateByHash(hash util.Uint160) (*state.Contract, error)
}
// Info allows to get basic token info using RPC client.
func Info(c InfoClient, hash util.Uint160) (*wallet.Token, error) {
cs, err := c.GetContractStateByHash(hash)
if err != nil {
return nil, err
}
var standard string
for _, st := range cs.Manifest.SupportedStandards {
if st == manifest.NEP17StandardName || st == manifest.NEP11StandardName {
standard = st
break
}
}
if standard == "" {
return nil, fmt.Errorf("contract %s is not NEP-11/NEP17", hash.StringLE())
}
b := New(invoker.New(c, nil), hash)
symbol, err := b.Symbol()
if err != nil {
return nil, err
}
decimals, err := b.Decimals()
if err != nil {
return nil, err
}
return wallet.NewToken(hash, cs.Manifest.Name, symbol, int64(decimals), standard), nil
}

View file

@ -0,0 +1,147 @@
package neptoken
import (
"errors"
"testing"
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"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/stretchr/testify/require"
)
type rpcClient struct {
cnt int
cserr error
cs *state.Contract
inverrs []error
invs []*result.Invoke
}
func (r *rpcClient) InvokeContractVerify(contract util.Uint160, params []smartcontract.Parameter, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error) {
panic("not implemented")
}
func (r *rpcClient) InvokeFunction(contract util.Uint160, operation string, params []smartcontract.Parameter, signers []transaction.Signer) (*result.Invoke, error) {
e, i := r.inverrs[r.cnt], r.invs[r.cnt]
r.cnt = (r.cnt + 1) % len(r.invs)
return i, e
}
func (r *rpcClient) InvokeScript(script []byte, signers []transaction.Signer) (*result.Invoke, error) {
panic("not implemented")
}
func (r *rpcClient) TerminateSession(sessionID uuid.UUID) (bool, error) {
panic("not implemented")
}
func (r *rpcClient) TraverseIterator(sessionID, iteratorID uuid.UUID, maxItemsCount int) ([]stackitem.Item, error) {
panic("not implemented")
}
func (r *rpcClient) GetContractStateByHash(hash util.Uint160) (*state.Contract, error) {
return r.cs, r.cserr
}
func TestInfo(t *testing.T) {
c := &rpcClient{}
hash := util.Uint160{1, 2, 3}
// Error on contract state.
c.cserr = errors.New("")
_, err := Info(c, hash)
require.Error(t, err)
// Error on missing standard.
c.cserr = nil
c.cs = &state.Contract{
ContractBase: state.ContractBase{
Manifest: manifest.Manifest{
Name: "Vasiliy",
SupportedStandards: []string{"RFC 1149"},
},
},
}
_, err = Info(c, hash)
require.Error(t, err)
// Error on Symbol()
c.cs = &state.Contract{
ContractBase: state.ContractBase{
Manifest: manifest.Manifest{
Name: "Übertoken",
SupportedStandards: []string{"NEP-17"},
},
},
}
c.inverrs = []error{errors.New(""), nil}
c.invs = []*result.Invoke{nil, nil}
_, err = Info(c, hash)
require.Error(t, err)
// Error on Decimals()
c.cnt = 0
c.inverrs[0], c.inverrs[1] = c.inverrs[1], c.inverrs[0]
c.invs[0] = &result.Invoke{
State: "HALT",
Stack: []stackitem.Item{
stackitem.Make("UBT"),
},
}
_, err = Info(c, hash)
require.Error(t, err)
// OK
c.cnt = 0
c.inverrs[1] = nil
c.invs[1] = &result.Invoke{
State: "HALT",
Stack: []stackitem.Item{
stackitem.Make(8),
},
}
ti, err := Info(c, hash)
require.NoError(t, err)
require.Equal(t, &wallet.Token{
Name: "Übertoken",
Hash: hash,
Decimals: 8,
Symbol: "UBT",
Standard: "NEP-17",
}, ti)
// NEP-11
c.cs = &state.Contract{
ContractBase: state.ContractBase{
Manifest: manifest.Manifest{
Name: "NFTizer",
SupportedStandards: []string{"NEP-11"},
},
},
}
c.cnt = 0
c.inverrs[1] = nil
c.invs[0] = &result.Invoke{
State: "HALT",
Stack: []stackitem.Item{
stackitem.Make("NZ"),
},
}
c.invs[1] = &result.Invoke{
State: "HALT",
Stack: []stackitem.Item{
stackitem.Make(0),
},
}
ti, err = Info(c, hash)
require.NoError(t, err)
require.Equal(t, &wallet.Token{
Name: "NFTizer",
Hash: hash,
Decimals: 0,
Symbol: "NZ",
Standard: "NEP-11",
}, ti)
}

View file

@ -36,6 +36,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neo" "github.com/nspcc-dev/neo-go/pkg/rpcclient/neo"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11" "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17" "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neptoken"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nns" "github.com/nspcc-dev/neo-go/pkg/rpcclient/nns"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/oracle" "github.com/nspcc-dev/neo-go/pkg/rpcclient/oracle"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/policy" "github.com/nspcc-dev/neo-go/pkg/rpcclient/policy"
@ -82,7 +83,7 @@ func TestClient_NEP17(t *testing.T) {
require.Equal(t, "RUB", sym) require.Equal(t, "RUB", sym)
}) })
t.Run("TokenInfo", func(t *testing.T) { t.Run("TokenInfo", func(t *testing.T) {
tok, err := c.NEP17TokenInfo(h) tok, err := neptoken.Info(c, h)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, h, tok.Hash) require.Equal(t, h, tok.Hash)
require.Equal(t, "Rubl", tok.Name) require.Equal(t, "Rubl", tok.Name)
@ -1259,7 +1260,7 @@ func TestClient_NEP11_ND(t *testing.T) {
require.Equal(t, "NNS", sym) require.Equal(t, "NNS", sym)
}) })
t.Run("TokenInfo", func(t *testing.T) { t.Run("TokenInfo", func(t *testing.T) {
tok, err := c.NEP11TokenInfo(h) tok, err := neptoken.Info(c, h)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, &wallet.Token{ require.Equal(t, &wallet.Token{
Name: "NameService", Name: "NameService",
@ -1345,7 +1346,7 @@ func TestClient_NEP11_D(t *testing.T) {
require.Equal(t, "NFSO", sym) require.Equal(t, "NFSO", sym)
}) })
t.Run("TokenInfo", func(t *testing.T) { t.Run("TokenInfo", func(t *testing.T) {
tok, err := c.NEP11TokenInfo(nfsoHash) tok, err := neptoken.Info(c, nfsoHash)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, &wallet.Token{ require.Equal(t, &wallet.Token{
Name: "NeoFS Object NFT", Name: "NeoFS Object NFT",