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

Support NEP-11 in RpcClient #739

Open
RichardBelsum opened this issue Jul 1, 2022 · 2 comments
Open

Support NEP-11 in RpcClient #739

RichardBelsum opened this issue Jul 1, 2022 · 2 comments

Comments

@RichardBelsum
Copy link
Contributor

RichardBelsum commented Jul 1, 2022

Support NEP-11 in RpcClient

e.g.

Nep17API

  • Task<byte> DecimalsAsync(UInt160 scriptHash)
  • Task<string> SymbolAsync(UInt160 scriptHash)
  • Task<BigInteger>TotalSupplyAsync(UInt160 scriptHash)
  • Task<RpcNep11TokenInfo> GetTokenInfoAsync(UInt160 scriptHash)
  • Task<BigInteger> BalanceOfAsync(UInt160 scriptHash, UInt160 account)
  • Task<UInt160> OwnerOfAsync(UInt160 scriptHash, ByteString tokenId)
  • Task<RpcNep11Propertie[]> PropertiesAsync(UInt160 scriptHash, ByteString tokenId)
  • Task<RpcNep11TokenInfo[]> TokensAsync(UInt160 scriptHash)
  • Task<RpcNep11TokenInfo[]> TokensOfAsync(UInt160 scriptHash, ByteString owner)
  • Task<Transaction> CreateTransferTxAsync(UInt160 scriptHash, KeyPair fromKey, UInt160 to, ByteString tokenId, object data = null)
  • Task<Transaction> CreateTransferTxAsync(UInt160 scriptHash, int m, ECPoint[] pubKeys, KeyPair[] fromKeys, UInt160 to, ByteString tokenId, object data = null)
@devhawk
Copy link
Contributor

devhawk commented Jul 7, 2022

Tokens/TokensOf should return IAsyncEnumerable.

Possible implementation (as extension methods):

public static IAsyncEnumerable<ByteString> TokensOfAsync(this RpcClient rpcClient, UInt160 scriptHash, UInt160 owner)
{
    const string METHOD = "tokensOf";
    var script = Neo.VM.Helper.MakeScript(scriptHash, "tokensOf", owner);
    return rpcClient.ParseTokenResults(script, METHOD);
}

public static IAsyncEnumerable<ByteString> TokensAsync(this RpcClient rpcClient, UInt160 scriptHash)
{
    const string METHOD = "tokens";
    var script = Neo.VM.Helper.MakeScript(scriptHash, METHOD);
    return rpcClient.ParseTokenResults(script, METHOD);
}

static async IAsyncEnumerable<ByteString> ParseTokenResults(this RpcClient rpcClient, byte[] script, string method = null)
{
    method ??= "InvokeScript";
    var result = await rpcClient.InvokeScriptAsync(script).ConfigureAwait(false);
    if (result.State != Neo.VM.VMState.HALT)
    {
        var message = string.IsNullOrEmpty(result.Exception) ? $"{method} returned {result.State}" : result.Exception;
        throw new Exception(message);
    }
    if (!string.IsNullOrEmpty(result.Session)
        && result.Stack.Length > 0
        && TryGetIteratorId(result.Stack[0], out var iteratorId))
    {
        await foreach (var json in rpcClient.TraverseIteratorAsync(result.Session, iteratorId))
        {
            yield return (ByteString)StackItemFromJson(json);
        }
    }

    throw new Exception($"{method} returned unexpected results");

    static bool TryGetIteratorId(Neo.VM.Types.StackItem item, out string iteratorId)
    {
        if (item is Neo.VM.Types.InteropInterface interop)
        {
            var @object = interop.GetInterface<object>();
            if (@object is JObject json)
            {
                iteratorId = json["id"]?.AsString() ?? "";
                if (json["interface"]?.AsString() == "IIterator"
                    && !string.IsNullOrEmpty(iteratorId))
                {
                    return true;
                }
            }
        }

        iteratorId = string.Empty;
        return false;
    }
}

@devhawk
Copy link
Contributor

devhawk commented Jul 7, 2022

And a possible implementation of PropertiesAsync

public static async Task<IReadOnlyDictionary<string, StackItem>> PropertiesAsync(this RpcClient rpcClient, UInt160 scriptHash, ByteString tokenId)
{
    const string METHOD = "properties";
    var script = Neo.VM.Helper.MakeScript(scriptHash, METHOD, tokenId.GetSpan().ToArray());
    var result = await rpcClient.InvokeScriptAsync(script);

    if (result.State != Neo.VM.VMState.HALT)
    {
        var message = string.IsNullOrEmpty(result.Exception) ? $"{METHOD} returned {result.State}" : result.Exception;
        throw new Exception(message);
    }
    if (result.Stack.Length > 0
        && result.Stack[0] is Neo.VM.Types.Map map)
    {
        return map.ToDictionary(kvp => kvp.Key.GetString(), kvp => kvp.Value);
    }

    throw new Exception($"{METHOD} returned unexpected results");
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants