Reputation: 23
I am working with the Web-API "Moralis-API" (https://docs.moralis.io/reference/getwalletnfts), where I get all NFTs from a NFT-Wallet. My API Call looks like this:
using System.Net.Http.Headers;
var client = new HttpClient();
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri("https://deep-index.moralis.io/api/v2/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/nft?chain=eth&format=decimal&normalizeMetadata=false"),
Headers =
{
{ "accept", "application/json" },
{ "X-API-Key", "test" },
},
};
using (var response = await client.SendAsync(request))
{
response.EnsureSuccessStatusCode();
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
}
The API returns a JSON-List which has NFTs with unique Token-IDs and a "cursor" which is a pagekey for Pagination. It looks like that:
{
"total": 1600,
"page": 1,
"page_size": 100,
** "cursor": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjdXN0b21QYXJhbXMiOnsid2FsbGV0QWRkcmVzcyI6IjB4ZDhkYTZiZjI2OTY0YWY5ZDdlZWQ5ZTAzZTUzNDE1ZDM3YWE5NjA0NSJ9LCJrZXlzIjpbIjE2NjY3NzIzMDkuNTQyIl0sIndoZXJlIjp7Im93bmVyX29mIjoiMHhkOGRhNmJmMjY5NjRhZjlkN2VlZDllMDNlNTM0MTVkMzdhYTk2MDQ1In0sImxpbWl0IjoxMDAsIm9mZnNldCI6MCwib3JkZXIiOltdLCJ0b3RhbCI6MTYwMCwicGFnZSI6MSwidGFpbE9mZnNldCI6NCwiaWF0IjoxNjY5MTI3OTUxfQ.UGpB3Qc88SJuU97dwVBwfYMkGcuH1-CaFbIKZ9iKots",
** "result": [
{
"token_address": "0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85",
"token_id": "103040680624633360426956226800459505851045291463662393946817594920946384752224",
"amount": "1",
"owner_of": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
"token_hash": "bc95dd2a065742fab91ddd09b2c9a6fc",
"block_number_minted": "16022477",
"block_number": "16022477",
"contract_type": "ERC721",
"name": "Ethereum Name Service",
"symbol": "ENS",
"token_uri": null,
"metadata": null,
"last_token_uri_sync": null,
"last_metadata_sync": "2022-11-22T02:13:59.639Z",
"minter_address": null
},
My problem is, I don't know, how to get the all NFTs. The API will give me only max 100 NFTs with each call, but I want to iterate to the next pages, to get all NFTs. I think there should be a solution with a loop. I didn't find any solution on Stack Overflow yet. Maybe someone can help me here.
Upvotes: 1
Views: 2434
Reputation: 471
According to the docs, the cursor
property is for paging results to the next set. It would seem like you need to deserialize the response object to grab both the results, and also the cursor property. I have been working on this a little more than I should have, but here's a proposed solution for your issue.
using System.Collections.Generic;
using System.Net.Http.Headers;
using System.Text;
using Newtonsoft.Json;
namespace Program
{
public static class Program
{
public static async Task Main(string[] args)
{
var client = new HttpClient();
var results = await GetAll(client);
Console.WriteLine(JsonConvert.SerializeObject(results));
}
public static async Task<IList<NftWalletResponseItem>> GetAll(HttpClient client)
{
var cursor = String.Empty;
var results = new List<NftWalletResponseItem>();
var builder = new UriBuilderExt("https", "deep-index.moralis.io")
.Path("/api/v2/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/nft")
.Query("chain", "eth")
.Query("format", "decimal")
.Query("normalizeMetadata", "false");
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = builder,
Headers =
{
{ "Accept", "application/json" },
{ "X-API-Key", "test" },
}
};
for(var previous = -1; previous < results.Count; )
{
previous = results.Count;
if(!String.IsNullOrWhiteSpace(cursor))
{
builder.Query("cursor", cursor);
}
var response = await PerformRequest(client, request);
results.AddRange(response.Result);
}
return results;
}
public static async Task<NftWalletResponse> PerformRequest(HttpClient client, HttpRequestMessage message)
{
var _default = new NftWalletResponse();
using (var response = await client.SendAsync(message))
{
if(response.IsSuccessStatusCode)
{
var body = await response.Content.ReadAsStringAsync();
NftWalletResponse result = JsonConvert.DeserializeObject<NftWalletResponse>(body) ?? _default;
return result;
}
}
return _default;
}
}
public class NftWalletResponse
{
[JsonProperty("total")]
public int Total { get; set; }
[JsonProperty("page")]
public int Page { get; set; }
[JsonProperty("page_size")]
public int PageSize { get; set; }
[JsonProperty("cursor")]
public string Cursor { get; set; }
[JsonProperty("result")]
public IList<NftWalletResponseItem> Result { get; set; }
public NftWalletResponse()
{
Cursor = String.Empty;
Result = new List<NftWalletResponseItem>();
}
}
public class NftWalletResponseItem
{
[JsonProperty("amount")]
public int Amount { get; set; }
[JsonProperty("block_number")]
public string BlockNumber { get; set; }
[JsonProperty("block_number_minted")]
public string BlockNumberMinted { get; set; }
[JsonProperty("contract_type")]
public string ContractType { get; set; }
[JsonProperty("last_metadata_sync")]
public DateTime LastMetadataSync { get; set; }
// [JsonProperty("last_token_uri_sync")]
// public <define type> LastTokenUriSync { get; set; }
// [JsonProperty("metadata")]
// public <define type> Metadata { get; set; }
// [JsonProperty("minter_address)]
// public <define type> MinterAddress { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("owner_of")]
public string OwnerOf { get; set; }
[JsonProperty("symbol")]
public string Symbol { get; set; }
[JsonProperty("token_address")]
public string TokenAddress { get; set; }
[JsonProperty("token_hash")]
public string TokenHash { get; set; }
[JsonProperty("token_id")]
public string TokenId { get; set; }
[JsonProperty("token_uri")]
public string TokenUri { get; set; }
public NftWalletResponseItem()
{
BlockNumber = String.Empty;
BlockNumberMinted = String.Empty;
ContractType = String.Empty;
LastMetadataSync = DateTime.UtcNow;
Name = String.Empty;
OwnerOf = String.Empty;
Symbol = String.Empty;
TokenAddress = String.Empty;
TokenHash = String.Empty;
TokenId = String.Empty;
TokenUri = String.Empty;
}
}
public static class ArrayExt {
public static void Deconstruct<T>(this T[] srcArray, out T a0) {
if (srcArray == null || srcArray.Length < 1)
throw new ArgumentException(nameof(srcArray));
a0 = srcArray[0];
}
public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1) {
if (srcArray == null || srcArray.Length < 2)
throw new ArgumentException(nameof(srcArray));
a0 = srcArray[0];
a1 = srcArray[1];
}
public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2) {
if (srcArray == null || srcArray.Length < 3)
throw new ArgumentException(nameof(srcArray));
a0 = srcArray[0];
a1 = srcArray[1];
a2 = srcArray[2];
}
public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2, out T a3) {
if (srcArray == null || srcArray.Length < 4)
throw new ArgumentException(nameof(srcArray));
a0 = srcArray[0];
a1 = srcArray[1];
a2 = srcArray[2];
a3 = srcArray[3];
}
public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2, out T a3, out T a4) {
if (srcArray == null || srcArray.Length < 5)
throw new ArgumentException(nameof(srcArray));
a0 = srcArray[0];
a1 = srcArray[1];
a2 = srcArray[2];
a3 = srcArray[3];
a4 = srcArray[4];
}
public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2, out T a3, out T a4, out T a5) {
if (srcArray == null || srcArray.Length < 6)
throw new ArgumentException(nameof(srcArray));
a0 = srcArray[0];
a1 = srcArray[1];
a2 = srcArray[2];
a3 = srcArray[3];
a4 = srcArray[4];
a5 = srcArray[5];
}
public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2, out T a3, out T a4, out T a5, out T a6) {
if (srcArray == null || srcArray.Length < 7)
throw new ArgumentException(nameof(srcArray));
a0 = srcArray[0];
a1 = srcArray[1];
a2 = srcArray[2];
a3 = srcArray[3];
a4 = srcArray[4];
a5 = srcArray[5];
a6 = srcArray[6];
}
public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2, out T a3, out T a4, out T a5, out T a6, out T a7) {
if (srcArray == null || srcArray.Length < 8)
throw new ArgumentException(nameof(srcArray));
a0 = srcArray[0];
a1 = srcArray[1];
a2 = srcArray[2];
a3 = srcArray[3];
a4 = srcArray[4];
a5 = srcArray[5];
a6 = srcArray[6];
a7 = srcArray[7];
}
}
public class UriBuilderExt
{
private UriBuilder _builder;
public UriBuilderExt()
{
_builder = new UriBuilder();
}
public UriBuilderExt(string uri)
{
_builder = new UriBuilder(uri);
}
public UriBuilderExt(string schemeName, string hostName)
{
_builder = new UriBuilder(schemeName, hostName);
}
public UriBuilderExt(string schemeName, string hostName, int port)
{
_builder = new UriBuilder(schemeName, hostName, port);
}
public UriBuilderExt(string schemeName, string hostName, int port, string path)
{
_builder = new UriBuilder(schemeName, hostName, port, path);
}
public UriBuilderExt(string schemeName, string hostName, int port, string path, string fragment)
{
_builder = new UriBuilder(schemeName, hostName, port, path, fragment);
}
public UriBuilderExt(Uri uri)
{
_builder = new UriBuilder(uri);
}
public static implicit operator UriBuilderExt(string uri)
{
return new UriBuilderExt(uri);
}
public static implicit operator UriBuilderExt(Uri uri)
{
return new UriBuilderExt(uri);
}
public static implicit operator Uri(UriBuilderExt builder)
{
return builder.Build();
}
public Uri Build()
{
return _builder.Uri;
}
public UriBuilderExt Fragment(string fragment)
{
_builder.Fragment = fragment;
return this;
}
public UriBuilderExt Host(string host)
{
_builder.Host = host;
return this;
}
public UriBuilderExt Password(string password)
{
_builder.Password = password;
return this;
}
public UriBuilderExt Path(string path)
{
_builder.Path = path;
return this;
}
public UriBuilderExt Port(int port)
{
_builder.Port = port;
return this;
}
public UriBuilderExt Query(string key, string value)
{
var qs = new Dictionary<string, string?>();
var builder = new StringBuilder();
foreach(var entry in _builder.Query.Split('&'))
{
var (k, v) = entry.Split('=');
qs.Add(k, v);
}
qs.Add(key, value);
foreach(var entry in qs)
{
if(builder.Length > 0)
{
builder.Append('&');
}
if(!String.IsNullOrEmpty(entry.Value))
{
builder.AppendJoin('=', entry.Key, entry.Value);
}
else
{
builder.Append(entry.Key);
}
}
_builder.Query = builder.ToString();
return this;
}
public UriBuilderExt Scheme(string scheme)
{
_builder.Scheme = scheme;
return this;
}
public UriBuilderExt UserName(string user)
{
_builder.UserName = user;
return this;
}
}
}
Forgive some of the over-engineering, but I've been working in JavaScript for a while, and I've come to expect some functionality in that regard, such as destructuring assignment from sequence types, and also having a less manual implementation for constructing URLs (Node has a wonderful API for URL.prototype.searchParams
).
Upvotes: 3