Skip to content

Commit

Permalink
Merge pull request #127 from WalletConnect/feat/ensure-type-is-valid
Browse files Browse the repository at this point in the history
feat: add type safe check when sending custom request / response types
  • Loading branch information
skibitsky authored Oct 12, 2023
2 parents 0476ae3 + 5719236 commit 43f5ea8
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 0 deletions.
27 changes: 27 additions & 0 deletions Core Modules/WalletConnectSharp.Common/Utils/TypeSafety.cs
Original file line number Diff line number Diff line change
@@ -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>(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<T, T>(testObject, Settings);
}

public static TR UnsafeJsonRewrap<T, TR>(this T source, JsonSerializerSettings settings = null)
{
var json = settings == null ?
JsonConvert.SerializeObject(source) :
JsonConvert.SerializeObject(source, settings);

return JsonConvert.DeserializeObject<TR>(json);
}
}
22 changes: 22 additions & 0 deletions WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -14,6 +15,7 @@ public class TypedMessageHandler : ITypedMessageHandler
{
private bool _initialized = false;
private Dictionary<string, DecodeOptions> _decodeOptionsMap = new Dictionary<string, DecodeOptions>();
private HashSet<string> _typeSafeCache = new HashSet<string>();

public EventDelegator Events { get; }

Expand Down Expand Up @@ -303,6 +305,8 @@ public DecodeOptions DecodeOptionForTopic(string topic)
/// <returns>The id of the request sent</returns>
public async Task<long> SendRequest<T, TR>(string topic, T parameters, long? expiry = null, EncodeOptions options = null)
{
EnsureTypeIsSerializerSafe(parameters);

var method = RpcMethodAttribute.MethodForType<T>();

var payload = new JsonRpcRequest<T>(method, parameters);
Expand Down Expand Up @@ -339,6 +343,8 @@ public async Task<long> SendRequest<T, TR>(string topic, T parameters, long? exp
/// <typeparam name="TR">The response type</typeparam>
public async Task SendResult<T, TR>(long id, string topic, TR result, EncodeOptions options = null)
{
EnsureTypeIsSerializerSafe(result);

var payload = new JsonRpcResponse<TR>(id, null, result);
var message = await this.Core.Crypto.Encode(topic, payload, options);
var opts = RpcResponseOptionsFromTypes<T, TR>();
Expand All @@ -356,6 +362,9 @@ public async Task SendResult<T, TR>(long id, string topic, TR result, EncodeOpti
/// <typeparam name="TR">The response type</typeparam>
public async Task SendError<T, TR>(long id, string topic, Error error, EncodeOptions options = null)
{
// Type Error is always serializer safe
// EnsureTypeIsSerializerSafe(error);

var payload = new JsonRpcResponse<TR>(id, error, default);
var message = await this.Core.Crypto.Encode(topic, payload, options);
var opts = RpcResponseOptionsFromTypes<T, TR>();
Expand All @@ -367,5 +376,18 @@ public void Dispose()
{
Events?.Dispose();
}

private void EnsureTypeIsSerializerSafe<T>(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);
}
}
}

0 comments on commit 43f5ea8

Please sign in to comment.