forked from TrueCloudLab/neoneo-go
rpc: add NEP11 commands which return iterator
These are Tokens (optional), TokensOf and OwnerOf (divisible).
This commit is contained in:
parent
9eeebf481c
commit
e27c894338
4 changed files with 148 additions and 1 deletions
|
@ -25,6 +25,7 @@ const (
|
||||||
totalSupplyPrefix = "s"
|
totalSupplyPrefix = "s"
|
||||||
accountPrefix = "a"
|
accountPrefix = "a"
|
||||||
tokenPrefix = "t"
|
tokenPrefix = "t"
|
||||||
|
tokensPrefix = "ts"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -76,6 +77,10 @@ func mkTokenKey(token []byte) []byte {
|
||||||
return append(res, token...)
|
return append(res, token...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mkTokensKey() []byte {
|
||||||
|
return []byte(tokensPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
// BalanceOf returns the number of tokens owned by specified address.
|
// BalanceOf returns the number of tokens owned by specified address.
|
||||||
func BalanceOf(holder interop.Hash160) int {
|
func BalanceOf(holder interop.Hash160) int {
|
||||||
if len(holder) != 20 {
|
if len(holder) != 20 {
|
||||||
|
@ -112,6 +117,36 @@ func setTokensOf(ctx storage.Context, holder interop.Hash160, tokens []string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setTokens saves minted token if it is not saved yet.
|
||||||
|
func setTokens(ctx storage.Context, newToken string) {
|
||||||
|
key := mkTokensKey()
|
||||||
|
var tokens = []string{}
|
||||||
|
val := storage.Get(ctx, key)
|
||||||
|
if val != nil {
|
||||||
|
tokens = std.Deserialize(val.([]byte)).([]string)
|
||||||
|
}
|
||||||
|
for i := 0; i < len(tokens); i++ {
|
||||||
|
if util.Equals(tokens[i], newToken) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tokens = append(tokens, newToken)
|
||||||
|
val = std.Serialize(tokens)
|
||||||
|
storage.Put(ctx, key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tokens returns an iterator that contains all of the tokens minted by the contract.
|
||||||
|
func Tokens() iterator.Iterator {
|
||||||
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
var arr = []string{}
|
||||||
|
key := mkTokensKey()
|
||||||
|
val := storage.Get(ctx, key)
|
||||||
|
if val != nil {
|
||||||
|
arr = std.Deserialize(val.([]byte)).([]string)
|
||||||
|
}
|
||||||
|
return iterator.Create(arr)
|
||||||
|
}
|
||||||
|
|
||||||
// TokensOf returns an iterator with all tokens held by specified address.
|
// TokensOf returns an iterator with all tokens held by specified address.
|
||||||
func TokensOf(holder interop.Hash160) iterator.Iterator {
|
func TokensOf(holder interop.Hash160) iterator.Iterator {
|
||||||
if len(holder) != 20 {
|
if len(holder) != 20 {
|
||||||
|
@ -219,6 +254,7 @@ func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) {
|
||||||
toksOf = append(toksOf, token)
|
toksOf = append(toksOf, token)
|
||||||
setTokensOf(ctx, from, toksOf)
|
setTokensOf(ctx, from, toksOf)
|
||||||
setOwnerOf(ctx, []byte(token), from)
|
setOwnerOf(ctx, []byte(token), from)
|
||||||
|
setTokens(ctx, token)
|
||||||
|
|
||||||
total++
|
total++
|
||||||
storage.Put(ctx, []byte(totalSupplyPrefix), total)
|
storage.Put(ctx, []byte(totalSupplyPrefix), total)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name: "HASHY NFT"
|
name: "HASHY NFT"
|
||||||
supportedstandards: ["NEP-11"]
|
supportedstandards: ["NEP-11"]
|
||||||
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf"]
|
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "tokens"]
|
||||||
events:
|
events:
|
||||||
- name: Transfer
|
- name: Transfer
|
||||||
parameters:
|
parameters:
|
||||||
|
|
|
@ -95,3 +95,38 @@ func topMapFromStack(st []stackitem.Item) (*stackitem.Map, error) {
|
||||||
}
|
}
|
||||||
return st[index].(*stackitem.Map), nil
|
return st[index].(*stackitem.Map), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// topIterableFromStack returns top list of elements of `resultItemType` type from stack.
|
||||||
|
func topIterableFromStack(st []stackitem.Item, resultItemType interface{}) ([]interface{}, error) {
|
||||||
|
index := len(st) - 1 // top stack element is last in the array
|
||||||
|
if t := st[index].Type(); t != stackitem.InteropT {
|
||||||
|
return nil, fmt.Errorf("invalid return stackitem type: %s (InteropInterface expected)", t.String())
|
||||||
|
}
|
||||||
|
iter, ok := st[index].Value().(result.Iterator)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("failed to deserialize iterable from interop stackitem: invalid value type (Array expected)")
|
||||||
|
}
|
||||||
|
result := make([]interface{}, len(iter.Values))
|
||||||
|
for i := range iter.Values {
|
||||||
|
switch resultItemType.(type) {
|
||||||
|
case string:
|
||||||
|
bytes, err := iter.Values[i].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to deserialize string from stackitem #%d: %w", i, err)
|
||||||
|
}
|
||||||
|
result[i] = string(bytes)
|
||||||
|
case util.Uint160:
|
||||||
|
bytes, err := iter.Values[i].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to deserialize uint160 from stackitem #%d: %w", i, err)
|
||||||
|
}
|
||||||
|
result[i], err = util.Uint160DecodeBytesBE(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode uint160 from stackitem #%d: %w", i, err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unsupported iterable type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
|
@ -85,6 +85,33 @@ func (c *Client) CreateNEP11TransferTx(acc *wallet.Account, tokenHash util.Uint1
|
||||||
}}, cosigners...))
|
}}, cosigners...))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NEP11TokensOf returns an array of token IDs for the specified owner of the specified NFT token.
|
||||||
|
func (c *Client) NEP11TokensOf(tokenHash util.Uint160, owner util.Uint160) ([]string, error) {
|
||||||
|
result, err := c.InvokeFunction(tokenHash, "tokensOf", []smartcontract.Parameter{
|
||||||
|
{
|
||||||
|
Type: smartcontract.Hash160Type,
|
||||||
|
Value: owner,
|
||||||
|
},
|
||||||
|
}, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = getInvocationError(result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
arr, err := topIterableFromStack(result.Stack, string(""))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get token IDs from stack: %w", err)
|
||||||
|
}
|
||||||
|
ids := make([]string, len(arr))
|
||||||
|
for i := range ids {
|
||||||
|
ids[i] = arr[i].(string)
|
||||||
|
}
|
||||||
|
return ids, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Non-divisible NFT methods section start.
|
// Non-divisible NFT methods section start.
|
||||||
|
|
||||||
// NEP11NDOwnerOf invokes `ownerOf` non-devisible NEP11 method with the
|
// NEP11NDOwnerOf invokes `ownerOf` non-devisible NEP11 method with the
|
||||||
|
@ -138,6 +165,33 @@ func (c *Client) NEP11DBalanceOf(tokenHash, owner util.Uint160, tokenID string)
|
||||||
return c.nepBalanceOf(tokenHash, owner, &tokenID)
|
return c.nepBalanceOf(tokenHash, owner, &tokenID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NEP11DOwnerOf returns list of the specified NEP11 divisible token owners.
|
||||||
|
func (c *Client) NEP11DOwnerOf(tokenHash util.Uint160, tokenID string) ([]util.Uint160, error) {
|
||||||
|
result, err := c.InvokeFunction(tokenHash, "ownerOf", []smartcontract.Parameter{
|
||||||
|
{
|
||||||
|
Type: smartcontract.StringType,
|
||||||
|
Value: tokenID,
|
||||||
|
},
|
||||||
|
}, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = getInvocationError(result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
arr, err := topIterableFromStack(result.Stack, util.Uint160{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get token IDs from stack: %w", err)
|
||||||
|
}
|
||||||
|
owners := make([]util.Uint160, len(arr))
|
||||||
|
for i := range owners {
|
||||||
|
owners[i] = arr[i].(util.Uint160)
|
||||||
|
}
|
||||||
|
return owners, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Divisible NFT methods section end.
|
// Divisible NFT methods section end.
|
||||||
|
|
||||||
// Optional NFT methods section start.
|
// Optional NFT methods section start.
|
||||||
|
@ -160,4 +214,26 @@ func (c *Client) NEP11Properties(tokenHash util.Uint160, tokenID string) (*stack
|
||||||
return topMapFromStack(result.Stack)
|
return topMapFromStack(result.Stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NEP11Tokens returns list of the tokens minted by the contract.
|
||||||
|
func (c *Client) NEP11Tokens(tokenHash util.Uint160) ([]string, error) {
|
||||||
|
result, err := c.InvokeFunction(tokenHash, "tokens", []smartcontract.Parameter{}, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = getInvocationError(result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
arr, err := topIterableFromStack(result.Stack, string(""))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get token IDs from stack: %w", err)
|
||||||
|
}
|
||||||
|
tokens := make([]string, len(arr))
|
||||||
|
for i := range tokens {
|
||||||
|
tokens[i] = arr[i].(string)
|
||||||
|
}
|
||||||
|
return tokens, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Optional NFT methods section end.
|
// Optional NFT methods section end.
|
||||||
|
|
Loading…
Reference in a new issue