Skip to content

Commit 07da310

Browse files
authored
Support Tuple Deserialization into Arrays & Lists (#89)
1 parent 0ccfbca commit 07da310

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

Thirdweb.Tests/Thirdweb.Contracts/Thirdweb.Contracts.Tests.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,26 @@ public async Task PrepareTest_NoSig()
102102
Assert.Contains("Method signature not found in contract ABI.", exception.Message);
103103
}
104104

105+
[Fact(Timeout = 120000)]
106+
public async Task ReadTest_TupleIntoArray()
107+
{
108+
var contract = await ThirdwebContract.Create(this.Client, "0xEb4AAB0253a50918a2Cbb7ADBaab78Ad19C07Bb1", 421614);
109+
var result = await contract.Read<List<object>>("getReserves");
110+
Assert.NotNull(result);
111+
Assert.NotEmpty(result);
112+
Assert.True(result.Count == 3);
113+
}
114+
115+
[Fact(Timeout = 120000)]
116+
public async Task ReadTest_TupleIntoList()
117+
{
118+
var contract = await ThirdwebContract.Create(this.Client, "0xEb4AAB0253a50918a2Cbb7ADBaab78Ad19C07Bb1", 421614);
119+
var result = await contract.Read<List<object>>("getReserves");
120+
Assert.NotNull(result);
121+
Assert.NotEmpty(result);
122+
Assert.True(result.Count == 3);
123+
}
124+
105125
private sealed class AllowlistProof
106126
{
107127
public List<byte[]> Proof { get; set; } = new List<byte[]>();

Thirdweb/Thirdweb.Contracts/ThirdwebContract.cs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
using System.Numerics;
2+
using Nethereum.ABI.FunctionEncoding;
3+
using Nethereum.ABI.Model;
24
using Nethereum.Contracts;
35
using Nethereum.Hex.HexTypes;
6+
using Newtonsoft.Json;
47

58
namespace Thirdweb;
69

@@ -119,7 +122,39 @@ public static async Task<T> Read<T>(ThirdwebContract contract, string method, pa
119122
var data = function.GetData(parameters);
120123
var resultData = await rpc.SendRequestAsync<string>("eth_call", new { to = contract.Address, data }, "latest").ConfigureAwait(false);
121124

122-
return function.DecodeTypeOutput<T>(resultData);
125+
if ((typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(List<>)) || typeof(T).IsArray)
126+
{
127+
var functionAbi = contractRaw.ContractBuilder.ContractABI.FindFunctionABIFromInputData(data);
128+
var decoder = new FunctionCallDecoder();
129+
var outputList = new FunctionCallDecoder().DecodeDefaultData(resultData.HexToBytes(), functionAbi.OutputParameters);
130+
var resultList = outputList.Select(x => x.Result).ToList();
131+
132+
if (typeof(T) == typeof(List<object>))
133+
{
134+
return (T)(object)resultList;
135+
}
136+
137+
if (typeof(T) == typeof(object[]))
138+
{
139+
return (T)(object)resultList.ToArray();
140+
}
141+
142+
try
143+
{
144+
var json = JsonConvert.SerializeObject(resultList);
145+
return JsonConvert.DeserializeObject<T>(json);
146+
}
147+
catch (Exception)
148+
{
149+
var dict = outputList.ConvertToObjectDictionary();
150+
var ser = JsonConvert.SerializeObject(dict.First().Value);
151+
return JsonConvert.DeserializeObject<T>(ser);
152+
}
153+
}
154+
else
155+
{
156+
return function.DecodeTypeOutput<T>(resultData);
157+
}
123158
}
124159

125160
/// <summary>

0 commit comments

Comments
 (0)