neo-modules
neo-modules copied to clipboard
Support NEP-11 in RpcClient
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)
Tokens/TokensOf should return IAsyncEnumerable<ByteString>.
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;
}
}
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");
}