Skip to content

Commit d11e4bc

Browse files
authored
Refactor read-only provider and add analytics (#120)
1 parent 5c37e2a commit d11e4bc

File tree

12 files changed

+301
-18
lines changed

12 files changed

+301
-18
lines changed

Assets/Thirdweb/Core/Scripts/AccountAbstraction/Core/BundlerClient.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ private static async Task<RpcResponseMessage> BundlerRequest(string url, string
4848
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, url) { Content = new StringContent(requestMessageJson, System.Text.Encoding.UTF8, "application/json") };
4949
if (new Uri(url).Host.EndsWith(".thirdweb.com"))
5050
{
51+
httpRequestMessage.Headers.Add("x-sdk-name", "UnitySDK");
52+
httpRequestMessage.Headers.Add("x-sdk-platform", Utils.GetRuntimePlatform());
53+
httpRequestMessage.Headers.Add("x-sdk-version", ThirdwebSDK.version);
5154
httpRequestMessage.Headers.Add("x-client-id", ThirdwebManager.Instance.SDK.session.Options.clientId);
5255
if (!Utils.IsWebGLBuild())
5356
httpRequestMessage.Headers.Add("x-bundle-id", ThirdwebManager.Instance.SDK.session.Options.bundleId);

Assets/Thirdweb/Core/Scripts/AccountAbstraction/Core/SmartWallet.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ internal async Task Initialize()
9393

9494
internal async Task UpdateDeploymentStatus()
9595
{
96-
var bytecode = await new Web3(ThirdwebManager.Instance.SDK.session.RPC).Eth.GetCode.SendRequestAsync(Accounts[0]);
96+
var bytecode = await Utils.GetWeb3().Eth.GetCode.SendRequestAsync(Accounts[0]);
9797
_deployed = bytecode != "0x";
9898
}
9999

@@ -125,7 +125,7 @@ internal async Task<bool> VerifySignature(byte[] hash, byte[] signature)
125125
return (new byte[] { }, 0);
126126

127127
var fn = new FactoryContract.CreateAccountFunction() { Admin = PersonalAddress, Data = new byte[] { } };
128-
var deployHandler = new Web3(ThirdwebManager.Instance.SDK.session.RPC).Eth.GetContractTransactionHandler<FactoryContract.CreateAccountFunction>();
128+
var deployHandler = Utils.GetWeb3().Eth.GetContractTransactionHandler<FactoryContract.CreateAccountFunction>();
129129
var txInput = await deployHandler.CreateTransactionInputEstimatingGasAsync(Config.factoryAddress, fn);
130130
var data = Utils.HexConcat(Config.factoryAddress, txInput.Data);
131131
return (data.HexStringToByteArray(), txInput.Gas.Value);

Assets/Thirdweb/Core/Scripts/Contract.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public async Task<CurrencyValue> GetBalance()
8585
}
8686
else
8787
{
88-
BigInteger balance = await ThirdwebManager.Instance.SDK.session.Web3.Eth.GetBalance.SendRequestAsync(address);
88+
BigInteger balance = await Utils.GetWeb3().Eth.GetBalance.SendRequestAsync(address);
8989
var cv = new CurrencyValue { value = balance.ToString(), displayValue = balance.ToString().ToEth() };
9090
return cv;
9191
}
@@ -114,7 +114,7 @@ public async Task<Transaction> Prepare(string functionName, string from = null,
114114
var initialInput = new TransactionInput();
115115
if (!Utils.IsWebGLBuild())
116116
{
117-
var contract = new Web3(ThirdwebManager.Instance.SDK.session.RPC).Eth.GetContract(this.abi, this.address);
117+
var contract = Utils.GetWeb3().Eth.GetContract(this.abi, this.address);
118118
var function = contract.GetFunction(functionName);
119119
var fromAddress = from ?? await ThirdwebManager.Instance.SDK.wallet.GetAddress();
120120
initialInput = function.CreateTransactionInput(fromAddress, args);
@@ -131,7 +131,7 @@ public async Task<Transaction> Prepare(string functionName, string from = null,
131131
/// <returns>The encoded function data as a string.</returns>
132132
public string Encode(string functionName, params object[] args)
133133
{
134-
var contract = new Web3(ThirdwebManager.Instance.SDK.session.RPC).Eth.GetContract(this.abi, this.address);
134+
var contract = Utils.GetWeb3().Eth.GetContract(this.abi, this.address);
135135
var function = contract.GetFunction(functionName);
136136
return function.GetData(args);
137137
}
@@ -144,7 +144,7 @@ public string Encode(string functionName, params object[] args)
144144
/// <returns>A list of <see cref="ParameterOutput"/> objects representing the decoded arguments.</returns>
145145
public List<ParameterOutput> Decode(string functionName, string encodedArgs)
146146
{
147-
var contract = new Web3(ThirdwebManager.Instance.SDK.session.RPC).Eth.GetContract(this.abi, this.address);
147+
var contract = Utils.GetWeb3().Eth.GetContract(this.abi, this.address);
148148
var function = contract.GetFunction(functionName);
149149
return function.DecodeInput(encodedArgs);
150150
}
@@ -156,7 +156,7 @@ public List<ParameterOutput> Decode(string functionName, string encodedArgs)
156156
public async Task<List<EventLog<TEventDTO>>> GetEventLogs<TEventDTO>(ulong? fromBlock = null, ulong? toBlock = null)
157157
where TEventDTO : IEventDTO, new()
158158
{
159-
var web3 = new Web3(ThirdwebManager.Instance.SDK.session.RPC);
159+
var web3 = Utils.GetWeb3();
160160
var transferEventHandler = web3.Eth.GetEvent<TEventDTO>(this.address);
161161
var filter = transferEventHandler.CreateFilterInput(
162162
fromBlock: fromBlock == null ? BlockParameter.CreateEarliest() : new BlockParameter(fromBlock.Value),
@@ -238,7 +238,7 @@ public async Task<T> Read<T>(string functionName, params object[] args)
238238
if (this.abi == null)
239239
throw new UnityException("You must pass an ABI for native platform custom calls");
240240

241-
var contract = new Web3(ThirdwebManager.Instance.SDK.session.RPC).Eth.GetContract(this.abi, this.address);
241+
var contract = Utils.GetWeb3().Eth.GetContract(this.abi, this.address);
242242
var function = contract.GetFunction(functionName);
243243
var result = await function.CallDecodingToDefaultAsync(args);
244244

Assets/Thirdweb/Core/Scripts/Storage/StorageDownloader.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@ public async Task<T> DownloadText<T>(string textURI)
2424

2525
using UnityWebRequest req = UnityWebRequest.Get(textURI);
2626
if (isThirdwebRequest)
27+
{
28+
req.SetRequestHeader("x-sdk-name", "UnitySDK");
29+
req.SetRequestHeader("x-sdk-platform", Utils.GetRuntimePlatform());
30+
req.SetRequestHeader("x-sdk-version", ThirdwebSDK.version);
2731
req.SetRequestHeader("x-client-id", ThirdwebManager.Instance.SDK.storage.ClientId);
32+
}
2833

2934
await req.SendWebRequest();
3035
if (req.result != UnityWebRequest.Result.Success)
@@ -51,7 +56,12 @@ public async Task<Sprite> DownloadImage(string imageURI)
5156

5257
using UnityWebRequest req = UnityWebRequestTexture.GetTexture(imageURI);
5358
if (isThirdwebRequest)
59+
{
60+
req.SetRequestHeader("x-sdk-name", "UnitySDK");
61+
req.SetRequestHeader("x-sdk-platform", Utils.GetRuntimePlatform());
62+
req.SetRequestHeader("x-sdk-version", ThirdwebSDK.version);
5463
req.SetRequestHeader("x-client-id", ThirdwebManager.Instance.SDK.storage.ClientId);
64+
}
5565

5666
await req.SendWebRequest();
5767
if (req.result != UnityWebRequest.Result.Success)

Assets/Thirdweb/Core/Scripts/Storage/StorageUploader.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ public async Task<IPFSUploadResult> UploadFromPath(string path)
4444
string result = "";
4545
using (UnityWebRequest pinReq = UnityWebRequest.Post(PIN_URI, form))
4646
{
47+
pinReq.SetRequestHeader("x-sdk-name", "UnitySDK");
48+
pinReq.SetRequestHeader("x-sdk-platform", Utils.GetRuntimePlatform());
49+
pinReq.SetRequestHeader("x-sdk-version", ThirdwebSDK.version);
4750
pinReq.SetRequestHeader("x-client-id", ThirdwebManager.Instance.SDK.storage.ClientId);
4851
if (!Utils.IsWebGLBuild())
4952
pinReq.SetRequestHeader("x-bundle-id", ThirdwebManager.Instance.SDK.session.Options.bundleId);
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
using System;
2+
using System.IO;
3+
using System.Net.Http;
4+
using System.Net.Http.Headers;
5+
using System.Text;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Nethereum.JsonRpc.Client.RpcMessages;
9+
using Nethereum.JsonRpc.Client;
10+
using Newtonsoft.Json;
11+
12+
namespace Thirdweb
13+
{
14+
public class ThirdwebClient : ClientBase
15+
{
16+
private const int NUMBER_OF_SECONDS_TO_RECREATE_HTTP_CLIENT = 60;
17+
public static int MaximumConnectionsPerServer { get; set; } = 20;
18+
private readonly Uri _baseUrl;
19+
private readonly HttpClientHandler _httpClientHandler;
20+
private readonly JsonSerializerSettings _jsonSerializerSettings;
21+
private volatile bool _firstHttpClient;
22+
private HttpClient _httpClient;
23+
private HttpClient _httpClient2;
24+
private bool _rotateHttpClients = true;
25+
private DateTime _httpClientLastCreatedAt;
26+
private readonly object _lockObject = new object();
27+
28+
public ThirdwebClient(Uri baseUrl, JsonSerializerSettings jsonSerializerSettings = null, HttpClientHandler httpClientHandler = null)
29+
{
30+
_baseUrl = baseUrl;
31+
32+
if (jsonSerializerSettings == null)
33+
jsonSerializerSettings = DefaultJsonSerializerSettingsFactory.BuildDefaultJsonSerializerSettings();
34+
35+
_jsonSerializerSettings = jsonSerializerSettings;
36+
_httpClientHandler = httpClientHandler;
37+
38+
#if NETCOREAPP2_1 || NETCOREAPP3_1 || NET5_0_OR_GREATER
39+
_httpClient = CreateNewHttpClient();
40+
_rotateHttpClients = false;
41+
#else
42+
CreateNewRotatedHttpClient();
43+
#endif
44+
}
45+
46+
private static HttpMessageHandler GetDefaultHandler()
47+
{
48+
try
49+
{
50+
#if NETSTANDARD2_0
51+
return new HttpClientHandler
52+
{
53+
MaxConnectionsPerServer = MaximumConnectionsPerServer
54+
};
55+
56+
#elif NETCOREAPP2_1 || NETCOREAPP3_1 || NET5_0_OR_GREATER
57+
return new SocketsHttpHandler
58+
{
59+
PooledConnectionLifetime = new TimeSpan(0, NUMBER_OF_SECONDS_TO_RECREATE_HTTP_CLIENT, 0),
60+
PooledConnectionIdleTimeout = new TimeSpan(0, NUMBER_OF_SECONDS_TO_RECREATE_HTTP_CLIENT, 0),
61+
MaxConnectionsPerServer = MaximumConnectionsPerServer
62+
};
63+
#else
64+
return null;
65+
#endif
66+
}
67+
catch
68+
{
69+
return null;
70+
}
71+
}
72+
73+
public ThirdwebClient(Uri baseUrl, HttpClient httpClient, AuthenticationHeaderValue authHeaderValue = null, JsonSerializerSettings jsonSerializerSettings = null)
74+
{
75+
_baseUrl = baseUrl;
76+
77+
if (authHeaderValue == null)
78+
{
79+
authHeaderValue = BasicAuthenticationHeaderHelper.GetBasicAuthenticationHeaderValueFromUri(baseUrl);
80+
}
81+
82+
if (jsonSerializerSettings == null)
83+
jsonSerializerSettings = DefaultJsonSerializerSettingsFactory.BuildDefaultJsonSerializerSettings();
84+
_jsonSerializerSettings = jsonSerializerSettings;
85+
InitialiseHttpClient(httpClient);
86+
_httpClient = httpClient;
87+
_rotateHttpClients = false;
88+
}
89+
90+
protected override async Task<RpcResponseMessage[]> SendAsync(RpcRequestMessage[] requests)
91+
{
92+
try
93+
{
94+
var httpClient = GetOrCreateHttpClient();
95+
var rpcRequestJson = JsonConvert.SerializeObject(requests, _jsonSerializerSettings);
96+
var httpContent = new StringContent(rpcRequestJson, Encoding.UTF8, "application/json");
97+
var cancellationTokenSource = new CancellationTokenSource();
98+
cancellationTokenSource.CancelAfter(ConnectionTimeout);
99+
100+
var httpResponseMessage = await httpClient.PostAsync(String.Empty, httpContent, cancellationTokenSource.Token).ConfigureAwait(false);
101+
httpResponseMessage.EnsureSuccessStatusCode();
102+
103+
var stream = await httpResponseMessage.Content.ReadAsStreamAsync().ConfigureAwait(false);
104+
using (var streamReader = new StreamReader(stream))
105+
using (var reader = new JsonTextReader(streamReader))
106+
{
107+
var serializer = JsonSerializer.Create(_jsonSerializerSettings);
108+
var messages = serializer.Deserialize<RpcResponseMessage[]>(reader);
109+
110+
return messages;
111+
}
112+
}
113+
catch (TaskCanceledException ex)
114+
{
115+
var exception = new RpcClientTimeoutException($"Rpc timeout after {ConnectionTimeout.TotalMilliseconds} milliseconds", ex);
116+
throw exception;
117+
}
118+
catch (Exception ex)
119+
{
120+
var exception = new RpcClientUnknownException("Error occurred when trying to send multiple rpc requests(s)", ex);
121+
throw exception;
122+
}
123+
}
124+
125+
protected override async Task<RpcResponseMessage> SendAsync(RpcRequestMessage request, string route = null)
126+
{
127+
try
128+
{
129+
var httpClient = GetOrCreateHttpClient();
130+
var rpcRequestJson = JsonConvert.SerializeObject(request, _jsonSerializerSettings);
131+
var httpContent = new StringContent(rpcRequestJson, Encoding.UTF8, "application/json");
132+
var cancellationTokenSource = new CancellationTokenSource();
133+
cancellationTokenSource.CancelAfter(ConnectionTimeout);
134+
135+
var httpResponseMessage = await httpClient.PostAsync(route, httpContent, cancellationTokenSource.Token).ConfigureAwait(false);
136+
httpResponseMessage.EnsureSuccessStatusCode();
137+
138+
var stream = await httpResponseMessage.Content.ReadAsStreamAsync().ConfigureAwait(false);
139+
using (var streamReader = new StreamReader(stream))
140+
using (var reader = new JsonTextReader(streamReader))
141+
{
142+
var serializer = JsonSerializer.Create(_jsonSerializerSettings);
143+
var message = serializer.Deserialize<RpcResponseMessage>(reader);
144+
return message;
145+
}
146+
}
147+
catch (TaskCanceledException ex)
148+
{
149+
var exception = new RpcClientTimeoutException($"Rpc timeout after {ConnectionTimeout.TotalMilliseconds} milliseconds", ex);
150+
throw exception;
151+
}
152+
catch (Exception ex)
153+
{
154+
var exception = new RpcClientUnknownException("Error occurred when trying to send rpc requests(s): " + request.Method, ex);
155+
throw exception;
156+
}
157+
}
158+
159+
private HttpClient GetOrCreateHttpClient()
160+
{
161+
if (_rotateHttpClients) //already created if not rotated
162+
{
163+
lock (_lockObject)
164+
{
165+
var timeSinceCreated = DateTime.UtcNow - _httpClientLastCreatedAt;
166+
if (timeSinceCreated.TotalSeconds > NUMBER_OF_SECONDS_TO_RECREATE_HTTP_CLIENT)
167+
CreateNewRotatedHttpClient();
168+
return GetClient();
169+
}
170+
}
171+
else
172+
{
173+
return GetClient();
174+
}
175+
}
176+
177+
private HttpClient GetClient()
178+
{
179+
if (_rotateHttpClients)
180+
{
181+
lock (_lockObject)
182+
{
183+
return _firstHttpClient ? _httpClient : _httpClient2;
184+
}
185+
}
186+
else
187+
{
188+
return _httpClient;
189+
}
190+
}
191+
192+
private void CreateNewRotatedHttpClient()
193+
{
194+
var httpClient = CreateNewHttpClient();
195+
_httpClientLastCreatedAt = DateTime.UtcNow;
196+
197+
if (_firstHttpClient)
198+
{
199+
lock (_lockObject)
200+
{
201+
_firstHttpClient = false;
202+
_httpClient2 = httpClient;
203+
}
204+
}
205+
else
206+
{
207+
lock (_lockObject)
208+
{
209+
_firstHttpClient = true;
210+
_httpClient = httpClient;
211+
}
212+
}
213+
}
214+
215+
private HttpClient CreateNewHttpClient()
216+
{
217+
HttpClient httpClient = new HttpClient();
218+
219+
if (_httpClientHandler != null)
220+
{
221+
httpClient = new HttpClient(_httpClientHandler);
222+
}
223+
else
224+
{
225+
var handler = GetDefaultHandler();
226+
if (handler != null)
227+
{
228+
httpClient = new HttpClient(handler);
229+
}
230+
}
231+
232+
InitialiseHttpClient(httpClient);
233+
return httpClient;
234+
}
235+
236+
private void InitialiseHttpClient(HttpClient httpClient)
237+
{
238+
httpClient.DefaultRequestHeaders.Add("x-sdk-name", "UnitySDK");
239+
httpClient.DefaultRequestHeaders.Add("x-sdk-platform", Utils.GetRuntimePlatform());
240+
httpClient.DefaultRequestHeaders.Add("x-sdk-version", ThirdwebSDK.version);
241+
httpClient.BaseAddress = _baseUrl;
242+
}
243+
}
244+
}

Assets/Thirdweb/Core/Scripts/ThirdwebClient.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Thirdweb/Core/Scripts/ThirdwebSDK.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,8 @@ public struct BiconomyOptions
238238

239239
public ThirdwebSession session;
240240

241+
internal const string version = "3.3.1";
242+
241243
/// <summary>
242244
/// Create an instance of the Thirdweb SDK.
243245
/// </summary>

0 commit comments

Comments
 (0)