Skip to content

Commit e69d6a4

Browse files
✨ Add rate limiter (#166)
* ✨ Add rate limiter * ⚡ Set defaults rate limit to 30/s
1 parent d1d7651 commit e69d6a4

File tree

4 files changed

+114
-2
lines changed

4 files changed

+114
-2
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Cysharp.Threading.Tasks;
4+
using Solana.Unity.Rpc.Utilities;
5+
6+
// ReSharper disable once CheckNamespace
7+
8+
namespace Solana.Unity.SDK
9+
{
10+
public class UnityRateLimiter : IRateLimiter
11+
{
12+
private int _hits;
13+
private int _durationMS;
14+
private readonly Queue<DateTime> _hitList;
15+
16+
public UnityRateLimiter(int hits, int duration_ms)
17+
{
18+
_hits = hits;
19+
_durationMS = duration_ms;
20+
_hitList = new Queue<DateTime>();
21+
}
22+
23+
public static UnityRateLimiter Create() => new UnityRateLimiter(30, 1000);
24+
25+
public bool CanFire()
26+
{
27+
DateTime utcNow = DateTime.UtcNow;
28+
DateTime dateTime = NextFireAllowed(utcNow);
29+
return utcNow >= dateTime;
30+
}
31+
32+
public async void Fire()
33+
{
34+
DateTime utcNow = DateTime.UtcNow;
35+
DateTime dateTime = NextFireAllowed(utcNow);
36+
await UniTask.Delay(dateTime.Subtract(utcNow));
37+
if (_durationMS <= 0)
38+
return;
39+
_hitList.Enqueue(DateTime.UtcNow);
40+
}
41+
42+
private DateTime NextFireAllowed(DateTime checkTime)
43+
{
44+
DateTime dateTime1 = checkTime;
45+
if (_durationMS == 0 || _hitList.Count == 0)
46+
return dateTime1;
47+
DateTime dateTime2 = checkTime.AddMilliseconds(-_durationMS);
48+
while (_hitList.Count > 0 && _hitList.Peek().Subtract(dateTime2).TotalMilliseconds < 0.0)
49+
_hitList.Dequeue();
50+
return _hitList.Count >= _hits ? _hitList.Peek().AddMilliseconds(_durationMS) : checkTime;
51+
}
52+
53+
public UnityRateLimiter PerSeconds(int seconds)
54+
{
55+
_durationMS = seconds * 1000;
56+
return this;
57+
}
58+
59+
public UnityRateLimiter PerMs(int ms)
60+
{
61+
_durationMS = ms;
62+
return this;
63+
}
64+
65+
public UnityRateLimiter AllowHits(int hits)
66+
{
67+
_hits = hits;
68+
return this;
69+
}
70+
71+
public override string ToString() => _hitList.Count > 0 ? string.Format("{0}-{1}", _hitList.Count, _hitList.Peek().ToString("HH:mm:ss.fff")) : "(empty)";
72+
}
73+
}

Runtime/codebase/UnityRateLimiter.cs.meta

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

Runtime/codebase/WalletBase.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ public abstract class WalletBase : IWalletBase
2828
{
2929
public const long SolLamports = 1000000000;
3030
public RpcCluster RpcCluster { get; }
31+
32+
public int RpcMaxHits = 30;
33+
public int RpcMaxHitsPerSeconds = 1;
3134

3235
private readonly Dictionary<int, Cluster> _rpcClusterMap = new ()
3336
{
@@ -320,11 +323,18 @@ private IRpcClient StartConnection()
320323
{
321324
if (_activeRpcClient == null && CustomRpcUri.IsNullOrEmpty())
322325
{
323-
_activeRpcClient = ClientFactory.GetClient(_rpcClusterMap[(int)RpcCluster]);
326+
_activeRpcClient = ClientFactory.GetClient(
327+
_rpcClusterMap[(int)RpcCluster],
328+
null,
329+
rateLimiter: UnityRateLimiter.Create().AllowHits(RpcMaxHits).PerSeconds(RpcMaxHitsPerSeconds)
330+
);
324331
}
325332
if (_activeRpcClient == null && !CustomRpcUri.IsNullOrEmpty())
326333
{
327-
_activeRpcClient = ClientFactory.GetClient(CustomRpcUri);
334+
_activeRpcClient = ClientFactory.GetClient(
335+
CustomRpcUri,
336+
null,
337+
rateLimiter: UnityRateLimiter.Create().AllowHits(RpcMaxHits).PerSeconds(RpcMaxHitsPerSeconds));
328338
}
329339

330340
return _activeRpcClient;

Runtime/codebase/Web3.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public WalletBase WalletBase {
3838
_wallet = value;
3939
if (currentWallet == null && value?.Account != null)
4040
{
41+
value.RpcMaxHits = RpcMaxHits;
42+
value.RpcMaxHitsPerSeconds = RpcMaxHitsPerSeconds;
4143
OnLogin?.Invoke(value.Account);
4244
UpdateBalance().Forget();
4345
if(OnNFTsUpdateInternal != null && AutoLoadNfts) UpdateNFTs().Forget();
@@ -52,6 +54,30 @@ public WalletBase WalletBase {
5254
private static WalletBase _wallet;
5355
private Web3AuthWallet _web3AuthWallet;
5456

57+
private int _defaultRpcMaxHits = 30;
58+
public int RpcMaxHits
59+
{
60+
61+
get => _wallet?.RpcMaxHits ?? _defaultRpcMaxHits;
62+
set
63+
{
64+
if (_wallet != null) _wallet.RpcMaxHits = value;
65+
else _defaultRpcMaxHits = value;
66+
}
67+
}
68+
69+
private int _defaultRpcMaxHitsPerSeconds = 1;
70+
public int RpcMaxHitsPerSeconds
71+
{
72+
73+
get => _wallet?.RpcMaxHitsPerSeconds ?? _defaultRpcMaxHitsPerSeconds;
74+
set
75+
{
76+
if (_wallet != null) _wallet.RpcMaxHitsPerSeconds = value;
77+
else _defaultRpcMaxHitsPerSeconds = value;
78+
}
79+
}
80+
5581
#endregion
5682

5783
#region Wallet Options

0 commit comments

Comments
 (0)