rpc: add client-side NEP-11 methods
This commit is contained in:
parent
7f48653e66
commit
1375918b63
5 changed files with 217 additions and 16 deletions
|
@ -29,6 +29,9 @@ Supported methods
|
||||||
getconnectioncount
|
getconnectioncount
|
||||||
getcontractstate
|
getcontractstate
|
||||||
getnativecontracts
|
getnativecontracts
|
||||||
|
getnep11balances
|
||||||
|
getnep11properties
|
||||||
|
getnep11transfers
|
||||||
getnep17balances
|
getnep17balances
|
||||||
getnep17transfers
|
getnep17transfers
|
||||||
getpeers
|
getpeers
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
@ -267,6 +269,16 @@ func (c *Client) GetNativeContracts() ([]state.NativeContract, error) {
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetNEP11Balances is a wrapper for getnep11balances RPC.
|
||||||
|
func (c *Client) GetNEP11Balances(address util.Uint160) (*result.NEP11Balances, error) {
|
||||||
|
params := request.NewRawParams(address.StringLE())
|
||||||
|
resp := new(result.NEP11Balances)
|
||||||
|
if err := c.performRequest("getnep11balances", params, resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetNEP17Balances is a wrapper for getnep17balances RPC.
|
// GetNEP17Balances is a wrapper for getnep17balances RPC.
|
||||||
func (c *Client) GetNEP17Balances(address util.Uint160) (*result.NEP17Balances, error) {
|
func (c *Client) GetNEP17Balances(address util.Uint160) (*result.NEP17Balances, error) {
|
||||||
params := request.NewRawParams(address.StringLE())
|
params := request.NewRawParams(address.StringLE())
|
||||||
|
@ -277,12 +289,53 @@ func (c *Client) GetNEP17Balances(address util.Uint160) (*result.NEP17Balances,
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNEP17Transfers is a wrapper for getnep17transfers RPC. Address parameter
|
// GetNEP11Properties is a wrapper for getnep11properties RPC. We recommend using
|
||||||
// is mandatory, while all the others are optional. Start and stop parameters
|
// NEP11Properties method instead of this to receive and work with proper VM types,
|
||||||
// are supported since neo-go 0.77.0 and limit and page since neo-go 0.78.0.
|
// this method is provided mostly for the sake of completeness. For well-known
|
||||||
// These parameters are positional in the JSON-RPC call, you can't specify limit
|
// attributes like "description", "image", "name" and "tokenURI" it returns strings,
|
||||||
// and not specify start/stop for example.
|
// while for all other ones []byte (which can be nil).
|
||||||
func (c *Client) GetNEP17Transfers(address string, start, stop *uint64, limit, page *int) (*result.NEP17Transfers, error) {
|
func (c *Client) GetNEP11Properties(asset util.Uint160, token []byte) (map[string]interface{}, error) {
|
||||||
|
params := request.NewRawParams(asset.StringLE(), hex.EncodeToString(token))
|
||||||
|
resp := make(map[string]interface{})
|
||||||
|
if err := c.performRequest("getnep11properties", params, &resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for k, v := range resp {
|
||||||
|
if v == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
str, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("value is not a string")
|
||||||
|
}
|
||||||
|
if result.KnownNEP11Properties[k] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val, err := base64.StdEncoding.DecodeString(str)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp[k] = val
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNEP11Transfers is a wrapper for getnep11transfers RPC. Address parameter
|
||||||
|
// is mandatory, while all the others are optional. Limit and page parameters are
|
||||||
|
// only supported by NeoGo servers and can only be specified with start and stop.
|
||||||
|
func (c *Client) GetNEP11Transfers(address string, start, stop *uint64, limit, page *int) (*result.NEP11Transfers, error) {
|
||||||
|
params, err := packTransfersParams(address, start, stop, limit, page)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp := new(result.NEP11Transfers)
|
||||||
|
if err := c.performRequest("getnep11transfers", *params, resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func packTransfersParams(address string, start, stop *uint64, limit, page *int) (*request.RawParams, error) {
|
||||||
params := request.NewRawParams(address)
|
params := request.NewRawParams(address)
|
||||||
if start != nil {
|
if start != nil {
|
||||||
params.Values = append(params.Values, *start)
|
params.Values = append(params.Values, *start)
|
||||||
|
@ -302,8 +355,21 @@ func (c *Client) GetNEP17Transfers(address string, start, stop *uint64, limit, p
|
||||||
} else if stop != nil || limit != nil || page != nil {
|
} else if stop != nil || limit != nil || page != nil {
|
||||||
return nil, errors.New("bad parameters")
|
return nil, errors.New("bad parameters")
|
||||||
}
|
}
|
||||||
|
return ¶ms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNEP17Transfers is a wrapper for getnep17transfers RPC. Address parameter
|
||||||
|
// is mandatory, while all the others are optional. Start and stop parameters
|
||||||
|
// are supported since neo-go 0.77.0 and limit and page since neo-go 0.78.0.
|
||||||
|
// These parameters are positional in the JSON-RPC call, you can't specify limit
|
||||||
|
// and not specify start/stop for example.
|
||||||
|
func (c *Client) GetNEP17Transfers(address string, start, stop *uint64, limit, page *int) (*result.NEP17Transfers, error) {
|
||||||
|
params, err := packTransfersParams(address, start, stop, limit, page)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
resp := new(result.NEP17Transfers)
|
resp := new(result.NEP17Transfers)
|
||||||
if err := c.performRequest("getnep17transfers", params, resp); err != nil {
|
if err := c.performRequest("getnep17transfers", *params, resp); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return resp, nil
|
return resp, nil
|
||||||
|
|
|
@ -532,6 +532,36 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"getnep11balances": {
|
||||||
|
{
|
||||||
|
name: "positive",
|
||||||
|
invoke: func(c *Client) (interface{}, error) {
|
||||||
|
hash, err := util.Uint160DecodeStringLE("1aada0032aba1ef6d1f07bbd8bec1d85f5380fb3")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return c.GetNEP11Balances(hash)
|
||||||
|
},
|
||||||
|
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"balance":[{"assethash":"a48b6e1291ba24211ad11bb90ae2a10bf1fcd5a8","tokens":[{"tokenid":"abcdef","amount":"1","lastupdatedblock":251604}]}],"address":"NcEkNmgWmf7HQVQvzhxpengpnt4DXjmZLe"}}`,
|
||||||
|
result: func(c *Client) interface{} {
|
||||||
|
hash, err := util.Uint160DecodeStringLE("a48b6e1291ba24211ad11bb90ae2a10bf1fcd5a8")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return &result.NEP11Balances{
|
||||||
|
Balances: []result.NEP11AssetBalance{{
|
||||||
|
Asset: hash,
|
||||||
|
Tokens: []result.NEP11TokenBalance{{
|
||||||
|
ID: "abcdef",
|
||||||
|
Amount: "1",
|
||||||
|
LastUpdated: 251604,
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
Address: "NcEkNmgWmf7HQVQvzhxpengpnt4DXjmZLe",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
"getnep17balances": {
|
"getnep17balances": {
|
||||||
{
|
{
|
||||||
name: "positive",
|
name: "positive",
|
||||||
|
@ -559,6 +589,61 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"getnep11properties": {
|
||||||
|
{
|
||||||
|
name: "positive",
|
||||||
|
invoke: func(c *Client) (interface{}, error) {
|
||||||
|
hash, err := util.Uint160DecodeStringLE("1aada0032aba1ef6d1f07bbd8bec1d85f5380fb3")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return c.GetNEP11Properties(hash, []byte("abcdef"))
|
||||||
|
}, // NcEkNmgWmf7HQVQvzhxpengpnt4DXjmZLe
|
||||||
|
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"name":"sometoken","field1":"c29tZXRoaW5n","field2":null}}`,
|
||||||
|
result: func(c *Client) interface{} {
|
||||||
|
return map[string]interface{}{
|
||||||
|
"name": "sometoken",
|
||||||
|
"field1": []byte("something"),
|
||||||
|
"field2": nil,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"getnep11transfers": {
|
||||||
|
{
|
||||||
|
name: "positive",
|
||||||
|
invoke: func(c *Client) (interface{}, error) {
|
||||||
|
return c.GetNEP11Transfers("NcEkNmgWmf7HQVQvzhxpengpnt4DXjmZLe", nil, nil, nil, nil)
|
||||||
|
},
|
||||||
|
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"sent":[],"received":[{"timestamp":1555651816,"assethash":"600c4f5200db36177e3e8a09e9f18e2fc7d12a0f","transferaddress":"NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP","amount":"1","tokenid":"abcdef","blockindex":436036,"transfernotifyindex":0,"txhash":"df7683ece554ecfb85cf41492c5f143215dd43ef9ec61181a28f922da06aba58"}],"address":"NcEkNmgWmf7HQVQvzhxpengpnt4DXjmZLe"}}`,
|
||||||
|
result: func(c *Client) interface{} {
|
||||||
|
assetHash, err := util.Uint160DecodeStringLE("600c4f5200db36177e3e8a09e9f18e2fc7d12a0f")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
txHash, err := util.Uint256DecodeStringLE("df7683ece554ecfb85cf41492c5f143215dd43ef9ec61181a28f922da06aba58")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return &result.NEP11Transfers{
|
||||||
|
Sent: []result.NEP11Transfer{},
|
||||||
|
Received: []result.NEP11Transfer{
|
||||||
|
{
|
||||||
|
Timestamp: 1555651816,
|
||||||
|
Asset: assetHash,
|
||||||
|
Address: "NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP",
|
||||||
|
Amount: "1",
|
||||||
|
ID: "abcdef",
|
||||||
|
Index: 436036,
|
||||||
|
NotifyIndex: 0,
|
||||||
|
TxHash: txHash,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Address: "NcEkNmgWmf7HQVQvzhxpengpnt4DXjmZLe",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
"getnep17transfers": {
|
"getnep17transfers": {
|
||||||
{
|
{
|
||||||
name: "positive",
|
name: "positive",
|
||||||
|
@ -1052,6 +1137,22 @@ type rpcClientErrorCase struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var rpcClientErrorCases = map[string][]rpcClientErrorCase{
|
var rpcClientErrorCases = map[string][]rpcClientErrorCase{
|
||||||
|
`{"jsonrpc":"2.0","id":1,"result":{"name":"name","bad":42}}`: {
|
||||||
|
{
|
||||||
|
name: "getnep11properties_unmarshalling_error",
|
||||||
|
invoke: func(c *Client) (interface{}, error) {
|
||||||
|
return c.GetNEP11Properties(util.Uint160{}, []byte{})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`{"jsonrpc":"2.0","id":1,"result":{"name":100500,"good":"c29tZXRoaW5n"}}`: {
|
||||||
|
{
|
||||||
|
name: "getnep11properties_unmarshalling_error",
|
||||||
|
invoke: func(c *Client) (interface{}, error) {
|
||||||
|
return c.GetNEP11Properties(util.Uint160{}, []byte{})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
`{"jsonrpc":"2.0","id":1,"result":"not-a-hex-string"}`: {
|
`{"jsonrpc":"2.0","id":1,"result":"not-a-hex-string"}`: {
|
||||||
{
|
{
|
||||||
name: "getblock_not_a_hex_response",
|
name: "getblock_not_a_hex_response",
|
||||||
|
@ -1229,12 +1330,30 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
|
||||||
return c.GetContractStateByHash(util.Uint160{})
|
return c.GetContractStateByHash(util.Uint160{})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "getnep11balances_invalid_params_error",
|
||||||
|
invoke: func(c *Client) (interface{}, error) {
|
||||||
|
return c.GetNEP11Balances(util.Uint160{})
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "getnep17balances_invalid_params_error",
|
name: "getnep17balances_invalid_params_error",
|
||||||
invoke: func(c *Client) (interface{}, error) {
|
invoke: func(c *Client) (interface{}, error) {
|
||||||
return c.GetNEP17Balances(util.Uint160{})
|
return c.GetNEP17Balances(util.Uint160{})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "getnep11properties_invalid_params_error",
|
||||||
|
invoke: func(c *Client) (interface{}, error) {
|
||||||
|
return c.GetNEP11Properties(util.Uint160{}, []byte{})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "getnep11transfers_invalid_params_error",
|
||||||
|
invoke: func(c *Client) (interface{}, error) {
|
||||||
|
return c.GetNEP11Transfers("", nil, nil, nil, nil)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "getnep17transfers_invalid_params_error",
|
name: "getnep17transfers_invalid_params_error",
|
||||||
invoke: func(c *Client) (interface{}, error) {
|
invoke: func(c *Client) (interface{}, error) {
|
||||||
|
@ -1416,12 +1535,24 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
|
||||||
return c.GetContractStateByHash(util.Uint160{})
|
return c.GetContractStateByHash(util.Uint160{})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "getnep11balances_unmarshalling_error",
|
||||||
|
invoke: func(c *Client) (interface{}, error) {
|
||||||
|
return c.GetNEP11Balances(util.Uint160{})
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "getnep17balances_unmarshalling_error",
|
name: "getnep17balances_unmarshalling_error",
|
||||||
invoke: func(c *Client) (interface{}, error) {
|
invoke: func(c *Client) (interface{}, error) {
|
||||||
return c.GetNEP17Balances(util.Uint160{})
|
return c.GetNEP17Balances(util.Uint160{})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "getnep11transfers_unmarshalling_error",
|
||||||
|
invoke: func(c *Client) (interface{}, error) {
|
||||||
|
return c.GetNEP11Transfers("", nil, nil, nil, nil)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "getnep17transfers_unmarshalling_error",
|
name: "getnep17transfers_unmarshalling_error",
|
||||||
invoke: func(c *Client) (interface{}, error) {
|
invoke: func(c *Client) (interface{}, error) {
|
||||||
|
|
|
@ -72,3 +72,11 @@ type NEP17Transfer struct {
|
||||||
NotifyIndex uint32 `json:"transfernotifyindex"`
|
NotifyIndex uint32 `json:"transfernotifyindex"`
|
||||||
TxHash util.Uint256 `json:"txhash"`
|
TxHash util.Uint256 `json:"txhash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KnownNEP11Properties contains a list of well-known NEP-11 token property names.
|
||||||
|
var KnownNEP11Properties = map[string]bool{
|
||||||
|
"description": true,
|
||||||
|
"image": true,
|
||||||
|
"name": true,
|
||||||
|
"tokenURI": true,
|
||||||
|
}
|
||||||
|
|
|
@ -159,13 +159,6 @@ var invalidBlockHeightError = func(index int, height int) *response.Error {
|
||||||
// doesn't set any Error function.
|
// doesn't set any Error function.
|
||||||
var upgrader = websocket.Upgrader{}
|
var upgrader = websocket.Upgrader{}
|
||||||
|
|
||||||
var knownNEP11Properties = map[string]bool{
|
|
||||||
"description": true,
|
|
||||||
"image": true,
|
|
||||||
"name": true,
|
|
||||||
"tokenURI": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new Server struct.
|
// New creates a new Server struct.
|
||||||
func New(chain blockchainer.Blockchainer, conf rpc.Config, coreServer *network.Server,
|
func New(chain blockchainer.Blockchainer, conf rpc.Config, coreServer *network.Server,
|
||||||
orc *oracle.Oracle, log *zap.Logger) Server {
|
orc *oracle.Oracle, log *zap.Logger) Server {
|
||||||
|
@ -783,12 +776,12 @@ func (s *Server) getNEP11Properties(ps request.Params) (interface{}, *response.E
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var val interface{}
|
var val interface{}
|
||||||
if knownNEP11Properties[string(key)] || kv.Value.Type() != stackitem.AnyT {
|
if result.KnownNEP11Properties[string(key)] || kv.Value.Type() != stackitem.AnyT {
|
||||||
v, err := kv.Value.TryBytes()
|
v, err := kv.Value.TryBytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if knownNEP11Properties[string(key)] {
|
if result.KnownNEP11Properties[string(key)] {
|
||||||
val = string(v)
|
val = string(v)
|
||||||
} else {
|
} else {
|
||||||
val = v
|
val = v
|
||||||
|
|
Loading…
Reference in a new issue