diff --git a/Thirdweb.Tests/Thirdweb.Utils/Thirdweb.Utils.Tests.cs b/Thirdweb.Tests/Thirdweb.Utils/Thirdweb.Utils.Tests.cs index 4bff4092..35afbb31 100644 --- a/Thirdweb.Tests/Thirdweb.Utils/Thirdweb.Utils.Tests.cs +++ b/Thirdweb.Tests/Thirdweb.Utils/Thirdweb.Utils.Tests.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Newtonsoft.Json.Linq; namespace Thirdweb.Tests.Utilities; @@ -506,7 +507,7 @@ public async void ToJsonExternalWalletFriendly_ReturnsCorrectValue4() var verifyingContract = await pkWallet.GetAddress(); // doesn't matter here var typedDataRaw = EIP712.GetTypedDefinition_SmartAccount_AccountMessage("Account", "1", 137, verifyingContract); var json = Utils.ToJsonExternalWalletFriendly(typedDataRaw, msg); - var jsonObject = Newtonsoft.Json.Linq.JObject.Parse(json); + var jsonObject = JObject.Parse(json); var internalMsg = jsonObject.SelectToken("$.message.message"); Assert.NotNull(internalMsg); Assert.Equal("0x01020304", internalMsg); @@ -763,4 +764,106 @@ public async Task FetchGasFees_Celo() Assert.True(maxPrio > 0); Assert.Equal(maxFee, maxPrio); } + + [Fact] + public void PreprocessTypedDataJson_FormatsBigIntegers() + { + // Arrange + var inputJson = + /*lang=json,strict*/ + @" + { + ""from"": 973250616940336452028326648501327235277017847475, + ""data"": ""0x"", + ""nested"": { + ""value"": 4294967295 + }, + ""array"": [ + 123, + 973250616940336452028326648501327235277017847475 + ] + }"; + + var expectedJson = + /*lang=json,strict*/ + @" + { + ""from"": ""973250616940336452028326648501327235277017847475"", + ""data"": ""0x"", + ""nested"": { + ""value"": 4294967295 + }, + ""array"": [ + 123, + ""973250616940336452028326648501327235277017847475"" + ] + }"; + + // Act + var processedJson = Utils.PreprocessTypedDataJson(inputJson); + + // Assert + var expectedJObject = JObject.Parse(expectedJson); + var processedJObject = JObject.Parse(processedJson); + + Assert.Equal(expectedJObject, processedJObject); + } + + [Fact] + public void PreprocessTypedDataJson_NoLargeNumbers_NoChange() + { + // Arrange + var inputJson = + /*lang=json,strict*/ + @" + { + ""value"": 123, + ""nested"": { + ""value"": 456 + }, + ""array"": [1, 2, 3] + }"; + + // Act + var processedJson = Utils.PreprocessTypedDataJson(inputJson); + var expectedJObject = JObject.Parse(inputJson); + var processedJObject = JObject.Parse(processedJson); + + // Assert + Assert.Equal(expectedJObject, processedJObject); + } + + [Fact] + public void PreprocessTypedDataJson_NestedLargeNumbers() + { + // Arrange + var inputJson = + /*lang=json,strict*/ + @" + { + ""nested"": { + ""value"": 973250616940336452028326648501327235277017847475 + }, + ""array"": [123, 973250616940336452028326648501327235277017847475] + }"; + + var expectedJson = + /*lang=json,strict*/ + @" + { + ""nested"": { + ""value"": ""973250616940336452028326648501327235277017847475"" + }, + ""array"": [123, ""973250616940336452028326648501327235277017847475""] + }"; + + // Act + var processedJson = Utils.PreprocessTypedDataJson(inputJson); + + // Assert + var expectedJObject = JObject.Parse(expectedJson); + var processedJObject = JObject.Parse(processedJson); + + Assert.Equal(expectedJObject, processedJObject); + } } diff --git a/Thirdweb/Thirdweb.Utils/Utils.cs b/Thirdweb/Thirdweb.Utils/Utils.cs index 4d955290..c4b32ad3 100644 --- a/Thirdweb/Thirdweb.Utils/Utils.cs +++ b/Thirdweb/Thirdweb.Utils/Utils.cs @@ -953,4 +953,54 @@ public static async Task GetSocialProfiles(ThirdwebClient client return new SocialProfiles(deserializedResponse.Data); } + + /// + /// Preprocesses the typed data JSON to stringify large numbers. + /// + /// The typed data JSON. + /// The preprocessed typed data JSON. + public static string PreprocessTypedDataJson(string json) + { + var jObject = JObject.Parse(json); + + static void StringifyLargeNumbers(JToken token) + { + if (token is JObject obj) + { + foreach (var property in obj.Properties().ToList()) + { + StringifyLargeNumbers(property.Value); + } + } + else if (token is JArray array) + { + foreach (var item in array.ToList()) + { + StringifyLargeNumbers(item); + } + } + else if (token is JValue value) + { + if (value.Type == JTokenType.Integer) + { + try + { + var bigInt = BigInteger.Parse(value.ToString()); + if (bigInt > new BigInteger(uint.MaxValue) || bigInt < BigInteger.Zero) + { + value.Replace(bigInt.ToString()); + } + } + catch (FormatException) + { + // Skip if the value isn't properly formatted as an integer + } + } + } + } + + StringifyLargeNumbers(jObject); + + return jObject.ToString(); + } } diff --git a/Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs b/Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs index c0888835..ba32ba78 100644 --- a/Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs +++ b/Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs @@ -776,9 +776,11 @@ public async Task SignTypedDataV4(string json) throw new ArgumentNullException(nameof(json), "Json to sign cannot be null."); } + var processedJson = Utils.PreprocessTypedDataJson(json); + var url = $"{ENCLAVE_PATH}/sign-typed-data"; - var requestContent = new StringContent(json, Encoding.UTF8, "application/json"); + var requestContent = new StringContent(processedJson, Encoding.UTF8, "application/json"); var response = await this.HttpClient.PostAsync(url, requestContent).ConfigureAwait(false); _ = response.EnsureSuccessStatusCode();