Skip to content

Commit 0dd395e

Browse files
authored
Smarter 1559 // Chain Utils // Caching (#53)
1 parent 9e3cbce commit 0dd395e

File tree

4 files changed

+187
-15
lines changed

4 files changed

+187
-15
lines changed

Thirdweb.Console/Program.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,23 @@
2222
Console.WriteLine($"Contract read result: {readResult}");
2323

2424
// Create wallets (this is an advanced use case, typically one wallet is plenty)
25-
// var privateKeyWallet = await PrivateKeyWallet.Create(client: client, privateKeyHex: privateKey);
26-
// var walletAddress = await privateKeyWallet.GetAddress();
25+
var privateKeyWallet = await PrivateKeyWallet.Create(client: client, privateKeyHex: privateKey);
26+
var walletAddress = await privateKeyWallet.GetAddress();
27+
Console.WriteLine($"Wallet address: {walletAddress}");
28+
29+
// // Self transfer 0 on chain 842
30+
// var tx = await ThirdwebTransaction.Create(
31+
// wallet: privateKeyWallet,
32+
// txInput: new ThirdwebTransactionInput()
33+
// {
34+
// From = await privateKeyWallet.GetAddress(),
35+
// To = await privateKeyWallet.GetAddress(),
36+
// Value = new HexBigInteger(BigInteger.Zero),
37+
// },
38+
// chainId: 842
39+
// );
40+
// var txHash = await ThirdwebTransaction.Send(tx);
41+
// Console.WriteLine($"Transaction hash: {txHash}");
2742

2843
// var chainData = await Utils.FetchThirdwebChainDataAsync(client, 421614);
2944
// Console.WriteLine($"Chain data: {JsonConvert.SerializeObject(chainData, Formatting.Indented)}");

Thirdweb.Tests/Thirdweb.Utils/Thirdweb.Utils.Tests.cs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,10 +438,10 @@ public void AdjustDecimals_ReturnsCorrectValue4()
438438
[Fact(Timeout = 120000)]
439439
public async Task FetchThirdwebChainDataAsync_ReturnsChainData_WhenResponseIsSuccessful()
440440
{
441+
var timer = System.Diagnostics.Stopwatch.StartNew();
441442
var chainId = new BigInteger(1);
442443

443444
var chainData = await Utils.FetchThirdwebChainDataAsync(_client, chainId);
444-
445445
Assert.NotNull(chainData);
446446
_ = Assert.IsType<ThirdwebChainData>(chainData);
447447

@@ -458,6 +458,17 @@ public async Task FetchThirdwebChainDataAsync_ReturnsChainData_WhenResponseIsSuc
458458
Assert.Equal(18, chainData.NativeCurrency.Decimals);
459459
Assert.NotNull(chainData.Faucets);
460460
Assert.NotNull(chainData.Explorers);
461+
462+
timer.Stop();
463+
var timeAttempt1 = timer.ElapsedMilliseconds;
464+
465+
timer.Restart();
466+
var chainData2 = await Utils.FetchThirdwebChainDataAsync(_client, chainId);
467+
Assert.NotNull(chainData2);
468+
_ = Assert.IsType<ThirdwebChainData>(chainData);
469+
470+
var timeAttempt2 = timer.ElapsedMilliseconds;
471+
Assert.True(timeAttempt1 > timeAttempt2);
461472
}
462473

463474
[Fact(Timeout = 120000)]
@@ -493,4 +504,44 @@ public async void ToJsonExternalWalletFriendly_ReturnsCorrectValue4()
493504
Assert.NotNull(internalMsg);
494505
Assert.Equal("0x01020304", internalMsg);
495506
}
507+
508+
[Fact(Timeout = 120000)]
509+
public async Task IsEip155Enforced_ReturnsTrue_WhenEIP155IsEnforced()
510+
{
511+
var timer = System.Diagnostics.Stopwatch.StartNew();
512+
var chainId = new BigInteger(842);
513+
514+
var isEnforced = await Utils.IsEip155Enforced(_client, chainId);
515+
Assert.True(isEnforced);
516+
517+
timer.Stop();
518+
var timeAttempt1 = timer.ElapsedMilliseconds;
519+
520+
timer.Restart();
521+
var isEnforcedCached = await Utils.IsEip155Enforced(_client, chainId);
522+
Assert.True(isEnforcedCached);
523+
524+
var timeAttempt2 = timer.ElapsedMilliseconds;
525+
Assert.True(timeAttempt1 > timeAttempt2);
526+
}
527+
528+
[Fact(Timeout = 120000)]
529+
public async Task IsEip155Enforced_ReturnsFalse_WhenEIP155IsNotEnforced()
530+
{
531+
var timer = System.Diagnostics.Stopwatch.StartNew();
532+
var chainId = new BigInteger(11155111);
533+
534+
var isEnforced = await Utils.IsEip155Enforced(_client, chainId);
535+
Assert.False(isEnforced);
536+
537+
timer.Stop();
538+
var timeAttempt1 = timer.ElapsedMilliseconds;
539+
540+
timer.Restart();
541+
var isEnforcedCached = await Utils.IsEip155Enforced(_client, chainId);
542+
Assert.False(isEnforcedCached);
543+
544+
var timeAttempt2 = timer.ElapsedMilliseconds;
545+
Assert.True(timeAttempt1 > timeAttempt2);
546+
}
496547
}

Thirdweb/Thirdweb.Transactions/ThirdwebTransaction.cs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ public static async Task<BigInteger> EstimateGasPrice(ThirdwebTransaction transa
238238
/// <param name="transaction">The transaction.</param>
239239
/// <param name="withBump">Whether to include a bump in the gas fees.</param>
240240
/// <returns>The estimated maximum fee per gas and maximum priority fee per gas.</returns>
241-
public static async Task<(BigInteger, BigInteger)> EstimateGasFees(ThirdwebTransaction transaction, bool withBump = true)
241+
public static async Task<(BigInteger maxFeePerGas, BigInteger maxPriorityFeePerGas)> EstimateGasFees(ThirdwebTransaction transaction, bool withBump = true)
242242
{
243243
var rpc = ThirdwebRPC.GetRpcInstance(transaction._wallet.Client, transaction.Input.ChainId.Value);
244244
var chainId = transaction.Input.ChainId.Value;
@@ -373,16 +373,23 @@ public static async Task<string> Send(ThirdwebTransaction transaction)
373373
transaction.Input.Value ??= new HexBigInteger(0);
374374
transaction.Input.Data ??= "0x";
375375
transaction.Input.Gas ??= new HexBigInteger(await EstimateGasLimit(transaction).ConfigureAwait(false));
376-
if (transaction.Input.GasPrice == null)
376+
377+
var supports1559 = Utils.IsEip1559Supported(transaction.Input.ChainId.Value.ToString());
378+
if (supports1559)
377379
{
378-
var (maxFeePerGas, maxPriorityFeePerGas) = await EstimateGasFees(transaction).ConfigureAwait(false);
379-
transaction.Input.MaxFeePerGas ??= maxFeePerGas.ToHexBigInteger();
380-
transaction.Input.MaxPriorityFeePerGas ??= maxPriorityFeePerGas.ToHexBigInteger();
380+
if (transaction.Input.GasPrice == null)
381+
{
382+
var (maxFeePerGas, maxPriorityFeePerGas) = await EstimateGasFees(transaction).ConfigureAwait(false);
383+
transaction.Input.MaxFeePerGas ??= new HexBigInteger(maxFeePerGas);
384+
transaction.Input.MaxPriorityFeePerGas ??= new HexBigInteger(maxPriorityFeePerGas);
385+
}
381386
}
382387
else
383388
{
384-
transaction.Input.MaxFeePerGas = null;
385-
transaction.Input.MaxPriorityFeePerGas = null;
389+
if (transaction.Input.MaxFeePerGas == null && transaction.Input.MaxPriorityFeePerGas == null)
390+
{
391+
transaction.Input.GasPrice ??= new HexBigInteger(await EstimateGasPrice(transaction).ConfigureAwait(false));
392+
}
386393
}
387394

388395
var rpc = ThirdwebRPC.GetRpcInstance(transaction._wallet.Client, transaction.Input.ChainId.Value);

Thirdweb/Thirdweb.Utils/Utils.cs

Lines changed: 104 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Nethereum.Util;
1010
using Newtonsoft.Json.Linq;
1111
using System.Text.RegularExpressions;
12+
using Newtonsoft.Json;
1213

1314
namespace Thirdweb
1415
{
@@ -17,6 +18,9 @@ namespace Thirdweb
1718
/// </summary>
1819
public static class Utils
1920
{
21+
private static readonly Dictionary<BigInteger, bool> Eip155EnforcedCache = new Dictionary<BigInteger, bool>();
22+
private static readonly Dictionary<BigInteger, ThirdwebChainData> ChainDataCache = new Dictionary<BigInteger, ThirdwebChainData>();
23+
2024
/// <summary>
2125
/// Computes the client ID from the given secret key.
2226
/// </summary>
@@ -322,6 +326,11 @@ public static BigInteger AdjustDecimals(this BigInteger value, int fromDecimals,
322326

323327
public static async Task<ThirdwebChainData> FetchThirdwebChainDataAsync(ThirdwebClient client, BigInteger chainId)
324328
{
329+
if (ChainDataCache.ContainsKey(chainId))
330+
{
331+
return ChainDataCache[chainId];
332+
}
333+
325334
if (client == null)
326335
{
327336
throw new ArgumentNullException(nameof(client));
@@ -337,17 +346,23 @@ public static async Task<ThirdwebChainData> FetchThirdwebChainDataAsync(Thirdweb
337346
{
338347
var response = await client.HttpClient.GetAsync(url).ConfigureAwait(false);
339348
var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
340-
var deserializedResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<ThirdwebChainDataResponse>(json);
349+
var deserializedResponse = JsonConvert.DeserializeObject<ThirdwebChainDataResponse>(json);
341350

342-
return deserializedResponse == null || deserializedResponse.Error != null
343-
? throw new Exception($"Failed to fetch chain data for chain ID {chainId}. Error: {Newtonsoft.Json.JsonConvert.SerializeObject(deserializedResponse?.Error)}")
344-
: deserializedResponse.Data;
351+
if (deserializedResponse == null || deserializedResponse.Error != null)
352+
{
353+
throw new Exception($"Failed to fetch chain data for chain ID {chainId}. Error: {JsonConvert.SerializeObject(deserializedResponse?.Error)}");
354+
}
355+
else
356+
{
357+
ChainDataCache[chainId] = deserializedResponse.Data;
358+
return deserializedResponse.Data;
359+
}
345360
}
346361
catch (HttpRequestException httpEx)
347362
{
348363
throw new Exception($"HTTP request error while fetching chain data for chain ID {chainId}: {httpEx.Message}", httpEx);
349364
}
350-
catch (Newtonsoft.Json.JsonException jsonEx)
365+
catch (JsonException jsonEx)
351366
{
352367
throw new Exception($"JSON deserialization error while fetching chain data for chain ID {chainId}: {jsonEx.Message}", jsonEx);
353368
}
@@ -487,5 +502,89 @@ private static List<JProperty> GetJProperties(string mainTypeName, MemberValue[]
487502

488503
return list;
489504
}
505+
506+
public static async Task<bool> IsEip155Enforced(ThirdwebClient client, BigInteger chainId)
507+
{
508+
if (Eip155EnforcedCache.ContainsKey(chainId))
509+
{
510+
return Eip155EnforcedCache[chainId];
511+
}
512+
513+
var result = false;
514+
var rpc = ThirdwebRPC.GetRpcInstance(client, chainId);
515+
516+
try
517+
{
518+
// Pre-155 tx that will fail
519+
var rawTransaction =
520+
"0xf8a58085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf31ba02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222";
521+
_ = await rpc.SendRequestAsync<string>("eth_sendRawTransaction", rawTransaction);
522+
}
523+
catch (Exception e)
524+
{
525+
var errorMsg = e.Message.ToLower();
526+
527+
var errorSubstrings = new List<string>
528+
{
529+
"eip-155",
530+
"eip155",
531+
"protected",
532+
"invalid chain id for signer",
533+
"chain id none",
534+
"chain_id mismatch",
535+
"recovered sender mismatch",
536+
"transaction hash mismatch",
537+
"chainid no support",
538+
"chainid (0)",
539+
"chainid(0)",
540+
"invalid sender"
541+
};
542+
543+
if (errorSubstrings.Any(errorMsg.Contains))
544+
{
545+
result = true;
546+
}
547+
else
548+
{
549+
// Check if all substrings in any of the composite substrings are present
550+
var errorSubstringsComposite = new List<string[]> { new[] { "account", "not found" }, new[] { "wrong", "chainid" } };
551+
552+
result = errorSubstringsComposite.Any(arr => arr.All(substring => errorMsg.Contains(substring)));
553+
}
554+
}
555+
556+
Eip155EnforcedCache[chainId] = result;
557+
return result;
558+
}
559+
560+
public static bool IsEip1559Supported(string chainId)
561+
{
562+
switch (chainId)
563+
{
564+
// BNB Mainnet
565+
case "56":
566+
// BNB Testnet
567+
case "97":
568+
// opBNB Mainnet
569+
case "204":
570+
// opBNB Testnet
571+
case "5611":
572+
// Oasys Mainnet
573+
case "248":
574+
// Oasys Testnet
575+
case "9372":
576+
// Vanar Mainnet
577+
case "2040":
578+
// Vanar Testnet (Vanguard)
579+
case "78600":
580+
// Taraxa Mainnet
581+
case "841":
582+
// Taraxa Testnet
583+
case "842":
584+
return false;
585+
default:
586+
return true;
587+
}
588+
}
490589
}
491590
}

0 commit comments

Comments
 (0)