diff --git a/.github/workflows/dotnet-ci.yml b/.github/workflows/dotnet-ci.yml index 6fdb482..540afd0 100644 --- a/.github/workflows/dotnet-ci.yml +++ b/.github/workflows/dotnet-ci.yml @@ -4,7 +4,7 @@ on: push: branches: [main] pull_request: - branches: [main] + types: [opened, synchronize] jobs: build-test-cov: @@ -42,6 +42,5 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} directory: ./ - flags: unittests, integrationtests verbose: true slug: thirdweb-dev/thirdweb-dotnet diff --git a/Thirdweb.Tests/Thirdweb.Client.Tests.cs b/Thirdweb.Tests/Thirdweb.Client.Tests.cs index 83d3732..a6c148d 100644 --- a/Thirdweb.Tests/Thirdweb.Client.Tests.cs +++ b/Thirdweb.Tests/Thirdweb.Client.Tests.cs @@ -83,10 +83,12 @@ public void SecretKeyAndBundleIdInitialization() [Fact] public void TimeoutOptions() { - var client = new ThirdwebClient(secretKey: _secretKey, fetchTimeoutOptions: new TimeoutOptions(storage: 30000, rpc: 30000)); + var client = new ThirdwebClient(secretKey: _secretKey, fetchTimeoutOptions: new TimeoutOptions(storage: 30000, rpc: 30000, other: 30000)); Assert.NotNull(client.FetchTimeoutOptions); Assert.Equal(30000, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Storage)); Assert.Equal(30000, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Rpc)); + Assert.Equal(30000, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Other)); + Assert.Equal(Constants.DEFAULT_FETCH_TIMEOUT, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Other + 1)); } [Fact] @@ -96,5 +98,7 @@ public void NoTimeoutOptions() Assert.NotNull(client.FetchTimeoutOptions); Assert.Equal(Constants.DEFAULT_FETCH_TIMEOUT, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Storage)); Assert.Equal(Constants.DEFAULT_FETCH_TIMEOUT, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Rpc)); + Assert.Equal(Constants.DEFAULT_FETCH_TIMEOUT, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Other)); + Assert.Equal(Constants.DEFAULT_FETCH_TIMEOUT, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Other + 1)); } } diff --git a/Thirdweb.Tests/Thirdweb.RPC.Tests.cs b/Thirdweb.Tests/Thirdweb.RPC.Tests.cs index dde31f8..462de40 100644 --- a/Thirdweb.Tests/Thirdweb.RPC.Tests.cs +++ b/Thirdweb.Tests/Thirdweb.RPC.Tests.cs @@ -1,4 +1,6 @@ -namespace Thirdweb.Tests; +using System.Numerics; + +namespace Thirdweb.Tests; public class RpcTests : BaseTests { @@ -32,4 +34,59 @@ public async Task TestTimeout() var exception = await Assert.ThrowsAsync(async () => await rpc.SendRequestAsync("eth_chainId")); _output.WriteLine($"TestTimeout Exception Message: {exception.Message}"); } + + [Fact] + public async Task TestBatch() + { + var client = new ThirdwebClient(secretKey: _secretKey); + var rpc = ThirdwebRPC.GetRpcInstance(client, 1); + var req = rpc.SendRequestAsync("eth_blockNumber"); + _ = await rpc.SendRequestAsync("eth_chainId"); + var blockNumberTasks = new List>(); + for (var i = 0; i < 100; i++) + { + blockNumberTasks.Add(rpc.SendRequestAsync("eth_blockNumber")); + } + var results = await Task.WhenAll(blockNumberTasks); + Assert.Equal(100, results.Length); + Assert.All(results, result => Assert.StartsWith("0x", result)); + Assert.All(results, result => Assert.Equal(results[0], result)); + } + + [Fact] + public async Task TestDeserialization() + { + var client = new ThirdwebClient(secretKey: _secretKey); + var rpc = ThirdwebRPC.GetRpcInstance(client, 1); + var exception = await Assert.ThrowsAsync(async () => await rpc.SendRequestAsync("eth_blockNumber")); + Assert.Equal("Failed to deserialize RPC response.", exception.Message); + } + + [Fact] + public void TestBadInitialization() + { + var clientException = Assert.Throws(() => ThirdwebRPC.GetRpcInstance(null, 0)); + Assert.Equal("client", clientException.ParamName); + var chainIdException = Assert.Throws(() => ThirdwebRPC.GetRpcInstance(new ThirdwebClient(secretKey: _secretKey), 0)); + Assert.Equal("Invalid Chain ID", chainIdException.Message); + } + + [Fact] + public async Task TestBundleIdRpc() + { + var client = new ThirdwebClient(clientId: _clientIdBundleIdOnly, bundleId: _bundleIdBundleIdOnly); + var rpc = ThirdwebRPC.GetRpcInstance(client, 1); + var blockNumber = await rpc.SendRequestAsync("eth_blockNumber"); + Assert.NotNull(blockNumber); + Assert.StartsWith("0x", blockNumber); + } + + [Fact] + public async Task TestRpcError() + { + var client = new ThirdwebClient(secretKey: _secretKey); + var rpc = ThirdwebRPC.GetRpcInstance(client, 1); + var exception = await Assert.ThrowsAsync(async () => await rpc.SendRequestAsync("eth_invalidMethod")); + Assert.Contains("RPC Error for request", exception.Message); + } } diff --git a/Thirdweb/Thirdweb.RPC/RpcError.cs b/Thirdweb/Thirdweb.RPC/RpcError.cs index 1d98691..2f43c74 100644 --- a/Thirdweb/Thirdweb.RPC/RpcError.cs +++ b/Thirdweb/Thirdweb.RPC/RpcError.cs @@ -9,8 +9,5 @@ public class RpcError [JsonProperty("message")] public string Message { get; set; } - - [JsonProperty("data")] - public object Data { get; set; } } } diff --git a/Thirdweb/Thirdweb.RPC/RpcTimer.cs b/Thirdweb/Thirdweb.RPC/RpcTimer.cs deleted file mode 100644 index cce66c4..0000000 --- a/Thirdweb/Thirdweb.RPC/RpcTimer.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Diagnostics; - -namespace Thirdweb -{ - public class RpcTimer - { - private Stopwatch stopwatch; - private readonly TimeSpan interval; - - public RpcTimer(TimeSpan interval) - { - this.interval = interval; - this.stopwatch = Stopwatch.StartNew(); - } - - public bool CheckTick() - { - Console.WriteLine($"Elapsed time: {stopwatch.ElapsedMilliseconds} ms"); - - if (stopwatch.Elapsed >= interval) - { - stopwatch.Restart(); - return true; - } - return false; - } - } -} diff --git a/Thirdweb/Thirdweb.RPC/ThirdwebRPC.cs b/Thirdweb/Thirdweb.RPC/ThirdwebRPC.cs index fa0cb0d..93b07cd 100644 --- a/Thirdweb/Thirdweb.RPC/ThirdwebRPC.cs +++ b/Thirdweb/Thirdweb.RPC/ThirdwebRPC.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Numerics; +using System.Numerics; using System.Text; -using System.Threading; -using System.Threading.Tasks; using Newtonsoft.Json; namespace Thirdweb @@ -31,6 +26,16 @@ public class ThirdwebRPC public static ThirdwebRPC GetRpcInstance(ThirdwebClient client, BigInteger chainId) { + if (client == null) + { + throw new ArgumentNullException(nameof(client)); + } + + if (chainId == 0) + { + throw new ArgumentException("Invalid Chain ID"); + } + var key = $"{client.ClientId}_{chainId}_{client.FetchTimeoutOptions.GetTimeout(TimeoutType.Rpc)}"; if (!_rpcs.ContainsKey(key)) @@ -99,11 +104,6 @@ static ThirdwebRPC() private ThirdwebRPC(ThirdwebClient client, BigInteger chainId) { - if (client == null) - throw new ArgumentNullException(nameof(client)); - if (chainId == 0) - throw new ArgumentException("Chain ID must be provided"); - _clientId = client.ClientId; _secretKey = client.SecretKey; _bundleId = client.BundleId; @@ -116,7 +116,9 @@ private ThirdwebRPC(ThirdwebClient client, BigInteger chainId) private void SendBatchNow() { if (_pendingBatch.Count == 0) + { return; + } List batchToSend; lock (_pendingBatch) @@ -134,11 +136,19 @@ private async Task SendBatchAsync(List batch) var requestMessage = new HttpRequestMessage(HttpMethod.Post, _rpcUrl) { Content = new StringContent(batchJson, Encoding.UTF8, "application/json") }; if (!string.IsNullOrEmpty(_clientId)) + { requestMessage.Headers.Add("x-client-id", _clientId); + } + if (!string.IsNullOrEmpty(_secretKey)) + { requestMessage.Headers.Add("x-secret-key", _secretKey); + } + if (!string.IsNullOrEmpty(_bundleId)) + { requestMessage.Headers.Add("x-bundle-id", _bundleId); + } try { @@ -154,11 +164,6 @@ private async Task SendBatchAsync(List batch) var responseJson = await response.Content.ReadAsStringAsync(); var responses = JsonConvert.DeserializeObject>>(responseJson); - if (responses == null) - { - throw new InvalidOperationException("Failed to deserialize RPC response."); - } - foreach (var rpcResponse in responses) { if (_responseCompletionSources.TryGetValue(rpcResponse.Id, out var tcs)) @@ -197,10 +202,5 @@ private async Task SendBatchAsync(List batch) } } } - - public void Dispose() - { - _batchSendTimer.Dispose(); - } } }