diff --git a/Core Modules/WalletConnectSharp.Common/Utils/TypeSafety.cs b/Core Modules/WalletConnectSharp.Common/Utils/TypeSafety.cs new file mode 100644 index 0000000..d74387d --- /dev/null +++ b/Core Modules/WalletConnectSharp.Common/Utils/TypeSafety.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json; + +namespace WalletConnectSharp.Common.Utils; + +public static class TypeSafety +{ + private static JsonSerializerSettings Settings = + new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Auto }; + + public static void EnsureTypeSerializerSafe(T testObject) + { + // unwrapping and rewrapping the object + // to / from JSON should tell us + // if it's serializer safe, since + // we are using the serializer to test + UnsafeJsonRewrap(testObject, Settings); + } + + public static TR UnsafeJsonRewrap(this T source, JsonSerializerSettings settings = null) + { + var json = settings == null ? + JsonConvert.SerializeObject(source) : + JsonConvert.SerializeObject(source, settings); + + return JsonConvert.DeserializeObject(json); + } +} diff --git a/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs b/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs index a29a37c..c542234 100644 --- a/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs +++ b/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json; using WalletConnectSharp.Common.Logging; using WalletConnectSharp.Common.Model.Errors; +using WalletConnectSharp.Common.Utils; using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Core.Models.Relay; using WalletConnectSharp.Crypto.Models; @@ -14,6 +15,7 @@ public class TypedMessageHandler : ITypedMessageHandler { private bool _initialized = false; private Dictionary _decodeOptionsMap = new Dictionary(); + private HashSet _typeSafeCache = new HashSet(); public EventDelegator Events { get; } @@ -303,6 +305,8 @@ public DecodeOptions DecodeOptionForTopic(string topic) /// The id of the request sent public async Task SendRequest(string topic, T parameters, long? expiry = null, EncodeOptions options = null) { + EnsureTypeIsSerializerSafe(parameters); + var method = RpcMethodAttribute.MethodForType(); var payload = new JsonRpcRequest(method, parameters); @@ -339,6 +343,8 @@ public async Task SendRequest(string topic, T parameters, long? exp /// The response type public async Task SendResult(long id, string topic, TR result, EncodeOptions options = null) { + EnsureTypeIsSerializerSafe(result); + var payload = new JsonRpcResponse(id, null, result); var message = await this.Core.Crypto.Encode(topic, payload, options); var opts = RpcResponseOptionsFromTypes(); @@ -356,6 +362,9 @@ public async Task SendResult(long id, string topic, TR result, EncodeOpti /// The response type public async Task SendError(long id, string topic, Error error, EncodeOptions options = null) { + // Type Error is always serializer safe + // EnsureTypeIsSerializerSafe(error); + var payload = new JsonRpcResponse(id, error, default); var message = await this.Core.Crypto.Encode(topic, payload, options); var opts = RpcResponseOptionsFromTypes(); @@ -367,5 +376,18 @@ public void Dispose() { Events?.Dispose(); } + + private void EnsureTypeIsSerializerSafe(T testObject) + { + var typeString = typeof(T).FullName; + if (_typeSafeCache.Contains(typeString)) + return; + + // Throw any serialization exceptions now + // before it's too late + TypeSafety.EnsureTypeSerializerSafe(testObject); + + _typeSafeCache.Add(typeString); + } } }