diff --git a/Core Modules/WalletConnectSharp.Common/Model/Errors/ErrorType.cs b/Core Modules/WalletConnectSharp.Common/Model/Errors/ErrorType.cs
index 994b30d..8330688 100644
--- a/Core Modules/WalletConnectSharp.Common/Model/Errors/ErrorType.cs
+++ b/Core Modules/WalletConnectSharp.Common/Model/Errors/ErrorType.cs
@@ -8,35 +8,6 @@ public enum ErrorType : uint
{
// 0 (Generic)
GENERIC = 0,
-
- // 10 (Internal)
- NON_CONFORMING_NAMESPACES = 9,
-
- // 1000 (Internal)
- MISSING_OR_INVALID = 1000,
- MISSING_RESPONSE = 1001,
- MISSING_DECRYPT_PARAMS = 1002,
- INVALID_UPDATE_REQUEST = 1003,
- INVALID_UPGRADE_REQUEST = 1004,
- INVALID_EXTEND_REQUEST = 1005,
- INVALID_STORAGE_KEY_NAME = 1020,
- RECORD_ALREADY_EXISTS = 1100,
- RESTORE_WILL_OVERRIDE = 1200,
- NO_MATCHING_ID = 1300,
- NO_MATCHING_TOPIC = 1301,
- NO_MATCHING_RESPONSE = 1302,
- NO_MATCHING_KEY = 1303,
- UNKNOWN_JSONRPC_METHOD = 1400,
- MISMATCHED_TOPIC = 1500,
- MISMATCHED_ACCOUNTS = 1501,
- SETTLED = 1600,
- NOT_APPROVED = 1601,
- PROPOSAL_RESPONDED = 1602,
- RESPONSE_ACKNOWLEDGED = 1603,
- EXPIRED = 1604,
- DELETED = 1605,
- RESUBSCRIBED = 1606,
- NOT_INITIALIZED = 1607,
// 2000 (Timeout)
SETTLE_TIMEOUT = 2000,
@@ -58,7 +29,7 @@ public enum ErrorType : uint
JSONRPC_REQUEST_METHOD_UNSUPPORTED = 4200,
DISCONNECTED_ALL_CHAINS = 4900,
DISCONNECTED_TARGET_CHAIN = 4901,
-
+
// 5000 (CAIP-25)
DISAPPROVED_CHAINS = 5000,
DISAPPROVED_JSONRPC = 5001,
@@ -67,9 +38,14 @@ public enum ErrorType : uint
UNSUPPORTED_JSONRPC = 5101,
UNSUPPORTED_NOTIFICATION = 5102,
UNSUPPORTED_ACCOUNTS = 5103,
- USER_DISCONNECTED = 5900,
+
+ // 6000 (Reason)
+ USER_DISCONNECTED = 6000,
+
+ // 8000 (Session)
+ SESSION_REQUEST_EXPIRED = 8000,
// 9000 (Unknown)
UNKNOWN = 9000,
}
-}
\ No newline at end of file
+}
diff --git a/Core Modules/WalletConnectSharp.Common/Model/Errors/ExpiredException.cs b/Core Modules/WalletConnectSharp.Common/Model/Errors/ExpiredException.cs
new file mode 100644
index 0000000..7689f7b
--- /dev/null
+++ b/Core Modules/WalletConnectSharp.Common/Model/Errors/ExpiredException.cs
@@ -0,0 +1,25 @@
+using System.Runtime.Serialization;
+
+namespace WalletConnectSharp.Common.Model.Errors;
+
+public class ExpiredException : Exception
+{
+ public ExpiredException()
+ {
+ }
+
+ public ExpiredException(string message)
+ : base(message)
+ {
+ }
+
+ public ExpiredException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ protected ExpiredException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+}
diff --git a/Core Modules/WalletConnectSharp.Common/Model/Errors/NamespacesException.cs b/Core Modules/WalletConnectSharp.Common/Model/Errors/NamespacesException.cs
new file mode 100644
index 0000000..d9e9942
--- /dev/null
+++ b/Core Modules/WalletConnectSharp.Common/Model/Errors/NamespacesException.cs
@@ -0,0 +1,25 @@
+using System.Runtime.Serialization;
+
+namespace WalletConnectSharp.Common.Model.Errors;
+
+public class NamespacesException : Exception
+{
+ public NamespacesException()
+ {
+ }
+
+ public NamespacesException(string message)
+ : base(message)
+ {
+ }
+
+ public NamespacesException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ protected NamespacesException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+}
diff --git a/Core Modules/WalletConnectSharp.Common/Model/Errors/SdkErrors.cs b/Core Modules/WalletConnectSharp.Common/Model/Errors/SdkErrors.cs
index db44bf6..fd1810e 100644
--- a/Core Modules/WalletConnectSharp.Common/Model/Errors/SdkErrors.cs
+++ b/Core Modules/WalletConnectSharp.Common/Model/Errors/SdkErrors.cs
@@ -1,7 +1,4 @@
-using System.Collections.Generic;
-using WalletConnectSharp.Common.Utils;
-
-namespace WalletConnectSharp.Common.Model.Errors
+namespace WalletConnectSharp.Common.Model.Errors
{
///
/// A helper class for generating error messages
@@ -9,47 +6,15 @@ namespace WalletConnectSharp.Common.Model.Errors
///
public static class SdkErrors
{
- private static readonly Dictionary DefaultParameters = new Dictionary()
- {
- {"topic", "undefined"},
- {"message", "Something went wrong"},
- {"name", "parameter"},
- {"context", "session"},
- {"blockchain", "Ethereum"}
- };
-
- ///
- /// Generate an error message using an ErrorType code and a dictionary
- /// of parameters for the error message
- /// An anonymous type can also be passed, which will be converted to a
- /// dictionary
- ///
- /// The error type message to generate
- /// A dictionary (or anonymous type) of parameters for the error message
- /// The error message as a string
- public static string MessageFromType(ErrorType type, Dictionary @params = null)
- {
- return MessageFromType(type, null, @params);
- }
-
///
/// Generate an error message using an ErrorType code, a message parameters
/// and a dictionary of parameters for the error message
///
/// The error type message to generate
- /// The message parameter
- /// A dictionary of parameters for the error message
+ /// Additional context
/// The error message as a string
- public static string MessageFromType(ErrorType type, string message = null, Dictionary @params = null)
+ public static string MessageFromType(ErrorType type, string context = null)
{
- if (@params == null)
- {
- @params = new Dictionary();
- }
-
- if (!string.IsNullOrWhiteSpace(message))
- @params.TryAdd("message", message);
-
string errorMessage;
switch (type)
{
@@ -57,92 +22,17 @@ public static string MessageFromType(ErrorType type, string message = null, Dict
case ErrorType.GENERIC:
errorMessage = "{message}";
break;
- case ErrorType.MISSING_OR_INVALID:
- errorMessage = "Missing or invalid {name}";
- break;
- case ErrorType.MISSING_RESPONSE:
- errorMessage = "Response is required for approved {context} proposals";
- break;
- case ErrorType.MISSING_DECRYPT_PARAMS:
- errorMessage = "Decrypt params required for {context}";
- break;
- case ErrorType.INVALID_UPDATE_REQUEST:
- errorMessage = "Invalid {context} update request";
- break;
- case ErrorType.INVALID_UPGRADE_REQUEST:
- errorMessage = "Invalid {context} upgrade request";
- break;
- case ErrorType.INVALID_EXTEND_REQUEST:
- errorMessage = "Invalid {context} extend request";
- break;
- case ErrorType.INVALID_STORAGE_KEY_NAME:
- errorMessage = "Invalid storage key name: {name}";
- break;
- case ErrorType.RECORD_ALREADY_EXISTS:
- errorMessage = "Record already exists for {context} matching id: {id}";
- break;
- case ErrorType.RESTORE_WILL_OVERRIDE:
- errorMessage = "Restore will override already set {context}";
- break;
- case ErrorType.NO_MATCHING_ID:
- errorMessage = "No matching {context} with id: {id}";
- break;
- case ErrorType.NO_MATCHING_TOPIC:
- errorMessage = "No matching {context} with topic {topic}";
- break;
- case ErrorType.NO_MATCHING_RESPONSE:
- errorMessage = "No response found in pending {context} proposal";
- break;
- case ErrorType.NO_MATCHING_KEY:
- errorMessage = "No matching key with tag: {tag}";
- break;
- case ErrorType.UNKNOWN_JSONRPC_METHOD:
- errorMessage = "Unknown JSON-RPC Method Requested: {method}";
- break;
- case ErrorType.MISMATCHED_TOPIC:
- errorMessage = "Mismatched topic for {context} with id: {id}";
- break;
- case ErrorType.MISMATCHED_ACCOUNTS:
- errorMessage = "Invalid accounts with mismatched chains: {mismatched}";
- break;
- case ErrorType.SETTLED:
- errorMessage = "{context} settled";
- break;
- case ErrorType.NOT_APPROVED:
- errorMessage = "{context} not approved";
- break;
- case ErrorType.PROPOSAL_RESPONDED:
- errorMessage = "{context} proposal responded";
- break;
- case ErrorType.RESPONSE_ACKNOWLEDGED:
- errorMessage = "{context} response acknowledge";
- break;
- case ErrorType.EXPIRED:
- errorMessage = "{context} expired";
- break;
- case ErrorType.DELETED:
- errorMessage = "{context} deleted";
- break;
- case ErrorType.RESUBSCRIBED:
- errorMessage = "Subscription resubscribed with topic: {topic}";
- break;
- case ErrorType.NOT_INITIALIZED:
- errorMessage = "{params} was not initialized";
- break;
- case ErrorType.SETTLE_TIMEOUT:
- errorMessage = "{context} failed to settle after {timeout} seconds";
- break;
case ErrorType.JSONRPC_REQUEST_TIMEOUT:
errorMessage = "JSON-RPC Request timeout after {timeout} seconds: {method}";
break;
case ErrorType.UNAUTHORIZED_TARGET_CHAIN:
- errorMessage = "Unauthorized Target ChainId Requested: {chainId}";
+ errorMessage = "Unauthorized Target ChainId Requested/";
break;
case ErrorType.UNAUTHORIZED_JSON_RPC_METHOD:
- errorMessage = "Unauthorized JSON-RPC Method Requested: {method}";
+ errorMessage = "Unauthorized JSON-RPC Method Requested.";
break;
case ErrorType.UNAUTHORIZED_NOTIFICATION_TYPE:
- errorMessage = "Unauthorized Notification Type Requested: {type}";
+ errorMessage = "Unauthorized Notification Type Requested.";
break;
case ErrorType.UNAUTHORIZED_UPDATE_REQUEST:
errorMessage = "Unauthorized {context} update request";
@@ -196,39 +86,21 @@ public static string MessageFromType(ErrorType type, string message = null, Dict
errorMessage = "{message}";
break;
case ErrorType.USER_DISCONNECTED:
- errorMessage = "User disconnected {context}";
+ errorMessage = "User disconnected.";
break;
case ErrorType.UNKNOWN:
errorMessage = "Unknown error {params}";
break;
- case ErrorType.NON_CONFORMING_NAMESPACES:
- errorMessage = @params["message"].ToString();
- break;
}
- errorMessage = FormatErrorText(errorMessage, DefaultParameters, @params);
-
- return errorMessage;
- }
-
- private static string FormatErrorText(string formattedText, Dictionary defaultArgs, Dictionary args = null)
- {
- if (args == null)
- args = new Dictionary();
-
- string text = formattedText;
-
- foreach (var key in args.Keys)
+ if (context == null)
{
- text = text.Replace("{" + key.ToLower() + "}", args[key].ToString());
+ return errorMessage;
}
-
- foreach (var key in defaultArgs.Keys)
+ else
{
- text = text.Replace("{" + key.ToLower() + "}", defaultArgs[key].ToString());
+ return $"{errorMessage} {context}";
}
-
- return text;
}
}
}
diff --git a/Core Modules/WalletConnectSharp.Common/Model/Errors/WalletConnectException.cs b/Core Modules/WalletConnectSharp.Common/Model/Errors/WalletConnectException.cs
index 32b063a..4c9559c 100644
--- a/Core Modules/WalletConnectSharp.Common/Model/Errors/WalletConnectException.cs
+++ b/Core Modules/WalletConnectSharp.Common/Model/Errors/WalletConnectException.cs
@@ -1,7 +1,4 @@
-using System;
-using System.Collections.Generic;
using Newtonsoft.Json;
-using WalletConnectSharp.Common.Utils;
namespace WalletConnectSharp.Common.Model.Errors
{
@@ -18,7 +15,7 @@ public class WalletConnectException : Exception
public uint Code { get; private set; }
///
- /// The error tyep of this exception
+ /// The error type of this exception
///
[JsonProperty("type")]
public string Type { get; private set; }
@@ -64,29 +61,17 @@ public WalletConnectException(string message, Exception innerException, ErrorTyp
/// exception
///
/// The error type of the exception
- /// The message parameter
- /// Additional (optional) parameters for the generated error message
+ /// An (optional) message for the error
+ /// An (optional) context for the error message
/// An (optional) inner exception that caused this exception
/// A new exception
- public static WalletConnectException FromType(ErrorType type, string message = null, Dictionary @params = null, Exception innerException = null)
+ public static WalletConnectException FromType(ErrorType type, string message = null, string context = null, Exception innerException = null)
{
- string errorMessage = SdkErrors.MessageFromType(type, message, @params);
+ var errorMessage = message ?? SdkErrors.MessageFromType(type, context);
- if (innerException != null)
- return new WalletConnectException(errorMessage, innerException, type);
- return new WalletConnectException(errorMessage, type);
- }
-
- ///
- /// A helper function that creates an exception given an ErrorType, and
- /// an (optional) dictionary of parameters for the error message
- ///
- /// The error type of the exception
- /// Additional (optional) parameters for the generated error message
- /// A new exception
- public static WalletConnectException FromType(ErrorType type, Dictionary @params = null)
- {
- return FromType(type, null, @params);
+ return innerException != null
+ ? new WalletConnectException(errorMessage, innerException, type)
+ : new WalletConnectException(errorMessage, type);
}
}
}
diff --git a/Core Modules/WalletConnectSharp.Common/Utils/UrlUtils.cs b/Core Modules/WalletConnectSharp.Common/Utils/UrlUtils.cs
index 3d3604e..71c17ff 100644
--- a/Core Modules/WalletConnectSharp.Common/Utils/UrlUtils.cs
+++ b/Core Modules/WalletConnectSharp.Common/Utils/UrlUtils.cs
@@ -1,5 +1,3 @@
-using System.Collections.Specialized;
-using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
@@ -12,43 +10,36 @@ namespace WalletConnectSharp.Common.Utils
public static class UrlUtils
{
///
- /// Parse query strings encoded parameters and return a NameValueCollection
+ /// Parse query strings encoded parameters and return a dictionary
///
/// The query string to parse
- /// The NameValueCollection containing all parameters in the query parameter
- public static NameValueCollection ParseQs(string queryString)
+ public static Dictionary ParseQs(string queryString)
{
- return Regex.Matches(queryString, "([^?=&]+)(=([^&]*))?")
- .ToDictionary(x => x.Groups[1].Value, x => x.Groups[3].Value)
- .Aggregate(new NameValueCollection(), (seed, current) =>
- {
- seed.Add(current.Key, current.Value);
- return seed;
- });
+ return Regex
+ .Matches(queryString, "([^?=&]+)(=([^&]*))?", RegexOptions.None, TimeSpan.FromMilliseconds(100))
+ .ToDictionary(x => x.Groups[1].Value, x => x.Groups[3].Value);
}
///
- /// Convert a NameValueCollection to a query string
+ /// Convert a dictionary to a query string
///
- /// The NameValueCollection to convert to a query string
- /// A query string containing all parameters from the NameValueCollection
- public static string StringifyQs(NameValueCollection @params)
+ /// A dictionary to convert to a query string
+ /// A query string containing all parameters from the dictionary
+ public static string StringifyQs(Dictionary @params)
{
- StringBuilder sb = new StringBuilder();
- foreach (var key in @params.AllKeys)
+ var sb = new StringBuilder();
+ foreach (var kvp in @params)
{
- var values = @params.GetValues(key);
- if (values == null)
- continue;
-
- foreach (var value in values)
+ if (kvp.Value == null)
{
- sb.Append(sb.Length == 0 ? "?" : "&");
- sb.AppendFormat("{0}={1}", WebUtility.UrlEncode(key), WebUtility.UrlEncode(value));
+ continue;
}
+
+ sb.Append(sb.Length == 0 ? "?" : "&");
+ sb.Append($"{WebUtility.UrlEncode(kvp.Key)}={WebUtility.UrlEncode(kvp.Value)}");
}
return sb.ToString();
}
}
-}
\ No newline at end of file
+}
diff --git a/Core Modules/WalletConnectSharp.Crypto/Crypto.cs b/Core Modules/WalletConnectSharp.Crypto/Crypto.cs
index 47dd5de..ec39cd9 100644
--- a/Core Modules/WalletConnectSharp.Crypto/Crypto.cs
+++ b/Core Modules/WalletConnectSharp.Crypto/Crypto.cs
@@ -159,11 +159,16 @@ public Task GenerateKeyPair()
generator.Init(options);
var keypair = generator.GenerateKeyPair();
- var publicKeyData = keypair.Public as X25519PublicKeyParameters;
- var privateKeyData = keypair.Private as X25519PrivateKeyParameters;
- if (publicKeyData == null || privateKeyData == null)
- throw new Exception("Could not generate keypair");
+ if (keypair.Public is not X25519PublicKeyParameters publicKeyData)
+ {
+ throw new InvalidCastException($"Public key is not an {nameof(X25519PublicKeyParameters)}");
+ }
+
+ if (keypair.Private is not X25519PrivateKeyParameters privateKeyData)
+ {
+ throw new InvalidCastException($"Private key is not an {nameof(X25519PrivateKeyParameters)}");
+ }
var publicKey = publicKeyData.GetEncoded().ToHex();
var privateKey = privateKeyData.GetEncoded().ToHex();
@@ -565,8 +570,7 @@ private void IsInitialized()
{
if (!this._initialized)
{
- throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED,
- new Dictionary() { { "Name", Name } });
+ throw new InvalidOperationException($"{nameof(Crypto)} module not initialized.");
}
}
@@ -654,12 +658,12 @@ private string DeserializeAndDecrypt(string symKey, string encoded)
private async Task GetClientSeed()
{
- var seed = "";
+ string seed;
try
{
seed = await KeyChain.Get(CryptoClientSeed);
}
- catch (Exception e)
+ catch (InvalidOperationException)
{
byte[] seedRaw = new byte[32];
RandomNumberGenerator.Fill(seedRaw);
diff --git a/Core Modules/WalletConnectSharp.Crypto/Encoder/BaseX.cs b/Core Modules/WalletConnectSharp.Crypto/Encoder/BaseX.cs
index 10df155..abac881 100644
--- a/Core Modules/WalletConnectSharp.Crypto/Encoder/BaseX.cs
+++ b/Core Modules/WalletConnectSharp.Crypto/Encoder/BaseX.cs
@@ -1,5 +1,3 @@
-using System;
-
namespace WalletConnectSharp.Crypto.Encoder
{
internal sealed class BaseX
@@ -14,9 +12,11 @@ internal sealed class BaseX
public BaseX(string alphabet, string name)
{
- if (alphabet.Length >= 255)
- throw new ArgumentException("Alphabet too long");
-
+ if (alphabet.Length >= 255)
+ {
+ throw new ArgumentException("Alphabet too long", nameof(alphabet));
+ }
+
this.name = name;
this.Alphabet = alphabet.ToCharArray();
@@ -70,7 +70,7 @@ public string Encode(byte[] source)
}
if (carry != 0)
{
- throw new Exception("Non-zero carry");
+ throw new InvalidOperationException("Non-zero carry");
}
length = i;
pbegin++;
@@ -126,7 +126,7 @@ public byte[] DecodeUnsafe(string source)
if (carry != 0)
{
- throw new Exception("Non-zero carry");
+ throw new InvalidOperationException("Non-zero carry");
}
length = i;
psz++;
@@ -155,7 +155,7 @@ public byte[] Decode(string source)
var buffer = DecodeUnsafe(source);
if (buffer != null)
return buffer;
- throw new Exception($"Non-{name} character");
+ throw new InvalidOperationException($"Non-{name} character");
}
}
}
diff --git a/Core Modules/WalletConnectSharp.Crypto/KeyChain.cs b/Core Modules/WalletConnectSharp.Crypto/KeyChain.cs
index d58720e..ba41e08 100644
--- a/Core Modules/WalletConnectSharp.Crypto/KeyChain.cs
+++ b/Core Modules/WalletConnectSharp.Crypto/KeyChain.cs
@@ -126,23 +126,15 @@ public async Task Set(string tag, string key)
}
///
- /// Get a saved key with the given tag. If no tag exists, then a WalletConnectException will
- /// be thrown.
+ /// Get a saved key with the given tag.
///
/// The tag of the key to retrieve
/// The key with the given tag
- /// Thrown if the given tag does not match any key
+ /// Thrown if the given tag does not match any key
public async Task Get(string tag)
{
- this.IsInitialized();
-
- if (!await Has(tag))
- {
- throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY, new Dictionary()
- {
- {"tag", tag}
- });
- }
+ IsInitialized();
+ await DoesTagExist(tag);
return this._keyChain[tag];
}
@@ -152,32 +144,29 @@ public async Task Get(string tag)
/// be thrown.
///
/// The tag of the key to delete
- /// Thrown if the given tag does not match any key
+ /// Thrown if the given tag does not match any key
public async Task Delete(string tag)
{
- this.IsInitialized();
-
- if (!await Has(tag))
- {
- throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY, new Dictionary()
- {
- {"tag", tag}
- });
- }
+ IsInitialized();
+ await DoesTagExist(tag);
_keyChain.Remove(tag);
-
await this.SaveKeyChain();
}
+ private async Task DoesTagExist(string tag)
+ {
+ if (!await Has(tag))
+ {
+ throw new InvalidOperationException($"Keychain does not contain key with tag: {tag}.");
+ }
+ }
+
private void IsInitialized()
{
if (!this._initialized)
{
- throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, new Dictionary()
- {
- {"Name", Name}
- });
+ throw new InvalidOperationException($"{nameof(Keychain)} module not initialized.");
}
}
diff --git a/Core Modules/WalletConnectSharp.Network/JsonRpcProvider.cs b/Core Modules/WalletConnectSharp.Network/JsonRpcProvider.cs
index 3266a59..099ed13 100644
--- a/Core Modules/WalletConnectSharp.Network/JsonRpcProvider.cs
+++ b/Core Modules/WalletConnectSharp.Network/JsonRpcProvider.cs
@@ -174,7 +174,7 @@ private void FinalizeConnection(IJsonRpcConnection connection)
public async Task Connect()
{
if (_connection == null)
- throw new Exception("No connection is set");
+ throw new InvalidOperationException("Connection is null");
await Connect(_connection);
}
diff --git a/Core Modules/WalletConnectSharp.Network/Models/Error.cs b/Core Modules/WalletConnectSharp.Network/Models/Error.cs
index aa68838..ed3a05b 100644
--- a/Core Modules/WalletConnectSharp.Network/Models/Error.cs
+++ b/Core Modules/WalletConnectSharp.Network/Models/Error.cs
@@ -1,7 +1,4 @@
-using System;
-using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
-using WalletConnectSharp.Common;
using WalletConnectSharp.Common.Model.Errors;
namespace WalletConnectSharp.Network.Models
@@ -28,30 +25,19 @@ public class Error
///
[JsonProperty("data")]
public string Data;
-
- ///
- /// Create an ErrorResponse with a given ErrorType and (optional) parameters
- ///
- /// The error type of the ErrorResponse to create
- /// The message to attach to the error
- /// A new ErrorResponse
- public static Error FromErrorType(ErrorType type, string message)
- {
- return FromErrorType(type, new Dictionary() { { "message", message } });
- }
///
/// Create an ErrorResponse with a given ErrorType and (optional) parameters
///
/// The error type of the ErrorResponse to create
- /// Extra parameters for the error message
+ /// Extra context
/// Extra data that is stored in the Data field of the newly created ErrorResponse
/// A new ErrorResponse
- public static Error FromErrorType(ErrorType type, Dictionary @params = null, string extraData = null)
+ public static Error FromErrorType(ErrorType type, string context = null, string extraData = null)
{
- string message = SdkErrors.MessageFromType(type, @params);
+ var message = SdkErrors.MessageFromType(type, context);
- return new Error()
+ return new Error
{
Code = (long) type,
Message = message,
@@ -66,7 +52,7 @@ public static Error FromErrorType(ErrorType type, Dictionary @pa
/// A new ErrorResponse object using values from the given exception
public static Error FromException(WalletConnectException walletConnectException)
{
- return new Error()
+ return new Error
{
Code = walletConnectException.Code,
Message = walletConnectException.Message,
diff --git a/Core Modules/WalletConnectSharp.Network/Models/RpcMethodAttribute.cs b/Core Modules/WalletConnectSharp.Network/Models/RpcMethodAttribute.cs
index 66099da..a00ac88 100644
--- a/Core Modules/WalletConnectSharp.Network/Models/RpcMethodAttribute.cs
+++ b/Core Modules/WalletConnectSharp.Network/Models/RpcMethodAttribute.cs
@@ -1,6 +1,3 @@
-using System;
-using System.Linq;
-
namespace WalletConnectSharp.Network.Models
{
///
@@ -25,25 +22,36 @@ public RpcMethodAttribute(string method)
{
MethodName = method;
}
-
+
///
- /// Get the method name that should be used for a given class type T. This is
- /// defined by the RpcMethodAttribute attached to the type T. If the type T has no
- /// RpcMethodAttribute, then an Exception is thrown
+ /// Retrieves the method name to be used for a given class type T, as defined by the RpcMethodAttribute
+ /// attached to type T. This method ensures that exactly one RpcMethodAttribute is present on the type T.
+ /// If no RpcMethodAttribute is found, or if multiple are found, an InvalidOperationException is thrown.
///
- /// The type T to get the method name for
- /// The method name to use as a string
- /// If the type T has no
- /// RpcMethodAttribute, then an Exception is thrown
+ /// The type T for which to get the method name.
+ /// The method name to use, as a string.
+ ///
+ /// Thrown if the type T has no RpcMethodAttribute defined,
+ /// or if multiple RpcMethodAttribute definitions are found.
+ ///
public static string MethodForType()
{
var attributes = typeof(T).GetCustomAttributes(typeof(RpcMethodAttribute), true);
- if (attributes.Length != 1)
- throw new Exception($"Type {typeof(T).FullName} has no WcMethod attribute!");
+ switch (attributes.Length)
+ {
+ case 0:
+ throw new InvalidOperationException($"Type {typeof(T).FullName} has no {nameof(RpcMethodAttribute)} defined.");
+ case > 1:
+ throw new InvalidOperationException($"Type {typeof(T).FullName} has multiple {nameof(RpcMethodAttribute)} definitions. Only one is allowed.");
+ }
- var method = attributes.Cast().First().MethodName;
+ var methodAttribute = attributes.Cast().SingleOrDefault();
+ if (methodAttribute == null)
+ {
+ throw new InvalidOperationException($"Type {typeof(T).FullName} has multiple RpcMethodAttribute definitions. Only one is allowed.");
+ }
- return method;
+ return methodAttribute.MethodName;
}
}
}
diff --git a/Core Modules/WalletConnectSharp.Storage/InMemoryStorage.cs b/Core Modules/WalletConnectSharp.Storage/InMemoryStorage.cs
index 46b0f2b..4c5d513 100644
--- a/Core Modules/WalletConnectSharp.Storage/InMemoryStorage.cs
+++ b/Core Modules/WalletConnectSharp.Storage/InMemoryStorage.cs
@@ -78,7 +78,7 @@ protected void IsInitialized()
{
if (!Initialized)
{
- throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, "Storage");
+ throw new InvalidOperationException($"{nameof(Storage)} module not initialized.");
}
}
diff --git a/Tests/WalletConnectSharp.Sign.Test/SignTests.cs b/Tests/WalletConnectSharp.Sign.Test/SignTests.cs
index 55d136a..f627043 100644
--- a/Tests/WalletConnectSharp.Sign.Test/SignTests.cs
+++ b/Tests/WalletConnectSharp.Sign.Test/SignTests.cs
@@ -159,7 +159,6 @@ public static async Task TestConnectMethod(ISignClient clientA, I
Assert.NotNull(proposal.OptionalNamespaces);
Assert.True(proposal.SessionProperties == null || proposal.SessionProperties.Count > 0);
Assert.NotNull(proposal.Expiry);
- Assert.NotNull(proposal.Id);
Assert.NotNull(proposal.Relays);
Assert.NotNull(proposal.Proposer);
Assert.NotNull(proposal.PairingTopic);
@@ -169,6 +168,9 @@ public static async Task TestConnectMethod(ISignClient clientA, I
var sessionData = await connectData.Approval;
await approveData.Acknowledged();
+ Assert.True(clientA.Find(dappConnectOptions.RequiredNamespaces).Length != 0);
+ Assert.True(clientA.Find(new RequiredNamespaces()).Length == 0);
+
return sessionData;
}
@@ -324,6 +326,75 @@ public async Task TestSessionRequestResponse()
Assert.Equal(eventResult, testData.a * testData.b);
Assert.Equal(eventResult, responseReturned.result);
}
+
+ [Fact] [Trait("Category", "integration")]
+ public async Task TestSessionRequestInvalidMethod()
+ {
+ await _cryptoFixture.WaitForClientsReady();
+
+ var validMethod = "test_method";
+
+ var dappConnectOptions = new ConnectOptions()
+ {
+ RequiredNamespaces = new RequiredNamespaces()
+ {
+ {
+ "eip155", new ProposedNamespace()
+ {
+ Methods = new[]
+ {
+ validMethod
+ },
+ Chains = new[]
+ {
+ "eip155:1",
+ "eip155:10"
+ },
+ Events = new[]
+ {
+ "chainChanged",
+ "accountsChanged"
+ }
+ }
+ }
+ }
+ };
+
+ var dappClient = ClientA;
+ var connectData = await dappClient.Connect(dappConnectOptions);
+
+ var walletClient = ClientB;
+ var proposal = await walletClient.Pair(connectData.Uri);
+
+ var approveData = await walletClient.Approve(proposal, "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045");
+
+ var sessionData = await connectData.Approval;
+ await approveData.Acknowledged();
+
+ var testData = new TestRequest2
+ {
+ x = "test", y = 4
+ };
+
+ // Use TestRequest2 which isn't included in the required namespaces
+ await Assert.ThrowsAsync(() => dappClient.Engine.Request(sessionData.Topic, testData));
+ }
+
+ [Fact] [Trait("Category", "integration")]
+ public async Task TestInvalidConnect()
+ {
+ await _cryptoFixture.WaitForClientsReady();
+ var dappClient = ClientA;
+
+ await Assert.ThrowsAsync(() => dappClient.Connect(null));
+
+ var connectOptions = new ConnectOptions
+ {
+ PairingTopic = "123"
+ };
+
+ await Assert.ThrowsAsync(() => dappClient.Connect(connectOptions));
+ }
[Fact, Trait("Category", "integration")]
public async Task TestTwoUniqueSessionRequestResponse()
diff --git a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs
index 1a0adb1..7698180 100644
--- a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs
+++ b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs
@@ -281,10 +281,7 @@ private async Task OnAuthResponse(string topic, JsonRpcResponse response)
{
this.Client.OnAuthResponse(new AuthErrorResponse()
{
- Id = id, Topic = topic, Error = Error.FromErrorType(ErrorType.GENERIC, new Dictionary()
- {
- {"Message", "Invalid signature"}
- })
+ Id = id, Topic = topic, Error = Error.FromErrorType(ErrorType.GENERIC, "Invalid signature")
});
}
else
@@ -390,7 +387,7 @@ private void IsInitialized()
{
if (!this.initialized)
{
- throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, Name);
+ throw new InvalidOperationException($"{nameof(AuthEngine)} module not initialized.");
}
}
diff --git a/WalletConnectSharp.Auth/Internals/AuthEngineValidations.cs b/WalletConnectSharp.Auth/Internals/AuthEngineValidations.cs
index 9210c71..f03aba4 100644
--- a/WalletConnectSharp.Auth/Internals/AuthEngineValidations.cs
+++ b/WalletConnectSharp.Auth/Internals/AuthEngineValidations.cs
@@ -24,7 +24,7 @@ internal bool IsValidRequest(RequestParams @params)
var expiry = @params.Expiry;
if (expiry != null && !Utils.IsValidRequestExpiry(expiry.Value, MinExpiry, MaxExpiry))
{
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID, $"request() expiry: {expiry}. Expiry must be a number (in seconds) between {MinExpiry} and {MaxExpiry}");
+ throw new ArgumentException($"Request expiry: {expiry}. Expiry must be a number (in seconds) between {MinExpiry} and {MaxExpiry}");
}
return validAudience && domainInAud && hasNonce && hasValidType;
diff --git a/WalletConnectSharp.Core/Controllers/Expirer.cs b/WalletConnectSharp.Core/Controllers/Expirer.cs
index 26d8f60..36eabff 100644
--- a/WalletConnectSharp.Core/Controllers/Expirer.cs
+++ b/WalletConnectSharp.Core/Controllers/Expirer.cs
@@ -1,4 +1,3 @@
-using WalletConnectSharp.Common.Model.Errors;
using WalletConnectSharp.Common.Utils;
using WalletConnectSharp.Core.Interfaces;
using WalletConnectSharp.Core.Models.Expirer;
@@ -19,7 +18,7 @@ public class Expirer : IExpirer
protected bool Disposed;
private Dictionary _expirations = new Dictionary();
- private bool initialized = false;
+ private bool initialized;
private Expiration[] _cached = Array.Empty();
private ICore _core;
@@ -286,7 +285,7 @@ private async Task Restore()
if (persisted.Length == 0) return;
if (_expirations.Count > 0)
{
- throw WalletConnectException.FromType(ErrorType.RESTORE_WILL_OVERRIDE, Name);
+ throw new InvalidOperationException($"Restoring will override existing data in {Name}.");
}
_cached = persisted;
@@ -294,10 +293,7 @@ private async Task Restore()
private Expiration GetExpiration(string target)
{
- if (!_expirations.ContainsKey(target))
- throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY, $"{Name}: {target}");
-
- return _expirations[target];
+ return _expirations.GetValueOrDefault(target);
}
private void CheckExpiry(string target, Expiration expiration)
@@ -326,7 +322,6 @@ private void CheckExpirations(object sender, EventArgs args)
private void RegisterEventListeners()
{
_core.HeartBeat.OnPulse += CheckExpirations;
- //_core.HeartBeat.On(HeartbeatEvents.Pulse, CheckExpirations);
this.Created += Persist;
this.Expired += Persist;
@@ -337,7 +332,7 @@ private void IsInitialized()
{
if (!initialized)
{
- throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, Name);
+ throw new InvalidOperationException($"{nameof(Expirer)} module not initialized.");
}
}
diff --git a/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs b/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs
index 1697e58..624c993 100644
--- a/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs
+++ b/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs
@@ -192,7 +192,7 @@ public Task> Get(string topic, long id)
throw WalletConnectException.FromType(ErrorType.MISMATCHED_TOPIC, $"{Name}: {id}");
}*/
- return Task.FromResult>(record);
+ return Task.FromResult(record);
}
///
@@ -243,19 +243,21 @@ public void Delete(string topic, long? id = null)
/// True if the request with the given topic and id exists, false otherwise
public Task Exists(string topic, long id)
{
+ IsInitialized();
+
+ if (_records.ContainsKey(id))
+ {
+ return Task.FromResult(false);
+ }
+
try
{
- IsInitialized();
- if (_records.ContainsKey(id)) return Task.FromResult(false);
var record = GetRecord(id);
-
return Task.FromResult(record.Topic == topic);
}
- catch (WalletConnectException e)
+ catch (KeyNotFoundException)
{
- if (e.CodeType == ErrorType.NO_MATCHING_KEY)
- return Task.FromResult(false);
- throw;
+ return Task.FromResult(false);
}
}
@@ -269,20 +271,19 @@ private async Task[]> GetJsonRpcRecords()
if (await _core.Storage.HasItem(StorageKey))
return await _core.Storage.GetItem[]>(StorageKey);
- return Array.Empty>();
+ return [];
}
private JsonRpcRecord GetRecord(long id)
{
IsInitialized();
- if (!_records.ContainsKey(id))
+ if (!_records.TryGetValue(id, out var record))
{
- throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY,
- new Dictionary() { { "Tag", $"{Name}: {id}" } });
+ throw new KeyNotFoundException($"No matching {Name} with id: {id}.");
}
- return _records[id];
+ return record;
}
private async Task Persist()
@@ -300,7 +301,7 @@ private async Task Restore()
return;
if (_records.Count > 0)
{
- throw WalletConnectException.FromType(ErrorType.RESTORE_WILL_OVERRIDE, Name);
+ throw new InvalidOperationException($"Restoring will override existing data in {Name}.");
}
_cached = persisted;
@@ -322,7 +323,7 @@ private void IsInitialized()
{
if (!_initialized)
{
- throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, Name);
+ throw new InvalidOperationException($"{nameof(JsonRpcHistory)} module not initialized.");
}
}
diff --git a/WalletConnectSharp.Core/Controllers/MessageTracker.cs b/WalletConnectSharp.Core/Controllers/MessageTracker.cs
index 242ce48..3127f44 100644
--- a/WalletConnectSharp.Core/Controllers/MessageTracker.cs
+++ b/WalletConnectSharp.Core/Controllers/MessageTracker.cs
@@ -202,7 +202,7 @@ private void IsInitialized()
{
if (!initialized)
{
- throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, this.Name);
+ throw new InvalidOperationException($"{nameof(MessageTracker)} module not initialized.");
}
}
diff --git a/WalletConnectSharp.Core/Controllers/Pairing.cs b/WalletConnectSharp.Core/Controllers/Pairing.cs
index f5017cf..1499310 100644
--- a/WalletConnectSharp.Core/Controllers/Pairing.cs
+++ b/WalletConnectSharp.Core/Controllers/Pairing.cs
@@ -126,7 +126,8 @@ private async Task RegisterTypedMessages()
public async Task Pair(string uri, bool activatePairing = true)
{
IsInitialized();
- await IsValidPair(uri);
+ ValidateUri(uri);
+
var uriParams = ParseUri(uri);
var topic = uriParams.Topic;
@@ -175,7 +176,7 @@ public UriParameters ParseUri(string uri)
var pathStart = uri.IndexOf(":", StringComparison.Ordinal);
int? pathEnd = uri.IndexOf("?", StringComparison.Ordinal) != -1
? uri.IndexOf("?", StringComparison.Ordinal)
- : (int?)null;
+ : null;
var protocol = uri.Substring(0, pathStart);
string path;
@@ -183,9 +184,8 @@ public UriParameters ParseUri(string uri)
else path = uri.Substring(pathStart + 1);
var requiredValues = path.Split("@");
- string queryString = pathEnd != null ? uri.Substring((int)pathEnd) : "";
- var queryParams = Regex.Matches(queryString, "([^?=&]+)(=([^&]*))?").Cast()
- .ToDictionary(x => x.Groups[1].Value, x => x.Groups[3].Value);
+ var queryString = pathEnd != null ? uri[(int)pathEnd..] : "";
+ var queryParams = UrlUtils.ParseQs(queryString);
var result = new UriParameters()
{
@@ -195,8 +195,7 @@ public UriParameters ParseUri(string uri)
SymKey = queryParams["symKey"],
Relay = new ProtocolOptions()
{
- Protocol = queryParams["relay-protocol"],
- Data = queryParams.ContainsKey("relay-data") ? queryParams["relay-data"] : null,
+ Protocol = queryParams["relay-protocol"], Data = queryParams.GetValueOrDefault("relay-data")
}
};
@@ -351,7 +350,7 @@ await Task.WhenAll(
private Task Cleanup()
{
List pairingTopics = (from pair in this.Store.Values.Where(e => e.Expiry != null)
- where Clock.IsExpired(pair.Expiry.Value)
+ where pair.Expiry != null && Clock.IsExpired(pair.Expiry.Value)
select pair.Topic).ToList();
return Task.WhenAll(
@@ -362,22 +361,22 @@ where Clock.IsExpired(pair.Expiry.Value)
private async Task IsValidPairingTopic(string topic)
{
if (string.IsNullOrWhiteSpace(topic))
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID,
- $"pairing topic should be a string {topic}");
+ {
+ throw new ArgumentNullException(nameof(topic));
+ }
if (!this.Store.Keys.Contains(topic))
- throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY,
- $"pairing topic doesn't exist {topic}");
+ throw new KeyNotFoundException($"Pairing topic {topic} not found.");
var expiry = this.Store.Get(topic).Expiry;
if (expiry != null && Clock.IsExpired(expiry.Value))
{
await DeletePairing(topic);
- throw WalletConnectException.FromType(ErrorType.EXPIRED, $"pairing topic: {topic}");
+ throw new ExpiredException($"Pairing topic {topic} has expired.");
}
}
- private bool IsValidUrl(string url)
+ private static bool IsValidUrl(string url)
{
if (string.IsNullOrWhiteSpace(url)) return false;
@@ -392,18 +391,17 @@ private bool IsValidUrl(string url)
}
}
- private Task IsValidPair(string uri)
+ private void ValidateUri(string uri)
{
if (!IsValidUrl(uri))
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID, $"pair() uri: {uri}");
- return Task.CompletedTask;
+ throw new FormatException($"Invalid URI format: {uri}");
}
private void IsInitialized()
{
if (!_initialized)
{
- throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, this.Name);
+ throw new InvalidOperationException($"{nameof(Pairing)} module not initialized.");
}
}
@@ -457,7 +455,7 @@ private async Task IsValidDisconnect(string topic, Error reason)
{
if (string.IsNullOrWhiteSpace(topic))
{
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID, $"disconnect() params: {topic}");
+ throw new ArgumentNullException(nameof(topic));
}
await IsValidPairingTopic(topic);
diff --git a/WalletConnectSharp.Core/Controllers/Relayer.cs b/WalletConnectSharp.Core/Controllers/Relayer.cs
index 8d63840..696cf34 100644
--- a/WalletConnectSharp.Core/Controllers/Relayer.cs
+++ b/WalletConnectSharp.Core/Controllers/Relayer.cs
@@ -227,9 +227,6 @@ private async void OnProviderDisconnected(object sender, EventArgs e)
if (this._transportExplicitlyClosed)
return;
- // Attempt to reconnect after one second
- await Task.Delay(1000);
-
await RestartTransport();
}
@@ -417,7 +414,9 @@ public async Task TransportOpen(string relayUrl = null)
void RejectTransportOpen(object sender, EventArgs @event)
{
- task2.TrySetException(new Exception("closeTransport called before connection was established"));
+ task2.TrySetException(
+ new IOException("The transport was closed before the connection was established.")
+ );
}
async void Task2()
@@ -490,7 +489,7 @@ protected virtual void IsInitialized()
{
if (!initialized)
{
- throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, Name);
+ throw new InvalidOperationException($"{nameof(Relayer)} module not initialized.");
}
}
diff --git a/WalletConnectSharp.Core/Controllers/Store.cs b/WalletConnectSharp.Core/Controllers/Store.cs
index 0e04122..9ff63b4 100644
--- a/WalletConnectSharp.Core/Controllers/Store.cs
+++ b/WalletConnectSharp.Core/Controllers/Store.cs
@@ -134,7 +134,7 @@ public async Task Init()
map.Add(value.Key, value);
}
- cached = Array.Empty();
+ cached = [];
initialized = true;
}
}
@@ -275,12 +275,12 @@ protected virtual async Task GetDataStore()
protected virtual TValue GetData(TKey key)
{
- if (!map.ContainsKey(key))
+ if (!map.TryGetValue(key, out var data))
{
- throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY, $"{Name}: {key}");
+ throw new KeyNotFoundException($"Key {key} not found in {Name}.");
}
- return map[key];
+ return data;
}
protected virtual Task Persist()
@@ -295,7 +295,7 @@ protected virtual async Task Restore()
if (persisted.Length == 0) return;
if (map.Count > 0)
{
- throw WalletConnectException.FromType(ErrorType.RESTORE_WILL_OVERRIDE, Name);
+ throw new InvalidOperationException($"Restoring will override existing data in {Name}.");
}
cached = persisted;
@@ -305,7 +305,7 @@ protected virtual void IsInitialized()
{
if (!initialized)
{
- throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, Name);
+ throw new InvalidOperationException($"{nameof(Store)} module not initialized.");
}
}
diff --git a/WalletConnectSharp.Core/Controllers/Subscriber.cs b/WalletConnectSharp.Core/Controllers/Subscriber.cs
index 2268fc9..0ee973e 100644
--- a/WalletConnectSharp.Core/Controllers/Subscriber.cs
+++ b/WalletConnectSharp.Core/Controllers/Subscriber.cs
@@ -1,5 +1,4 @@
using WalletConnectSharp.Common.Logging;
-using WalletConnectSharp.Common.Model.Errors;
using WalletConnectSharp.Common.Model.Relay;
using WalletConnectSharp.Common.Utils;
using WalletConnectSharp.Core.Interfaces;
@@ -249,7 +248,7 @@ protected virtual async Task Restore()
if (Subscriptions.Count > 0)
{
- throw WalletConnectException.FromType(ErrorType.RESTORE_WILL_OVERRIDE, Name);
+ throw new InvalidOperationException($"Restoring will override existing data in {Name}.");
}
_cached = persisted;
@@ -260,7 +259,6 @@ protected virtual async void CheckPending()
if (_relayer.TransportExplicitlyClosed)
return;
-
await BatchSubscribe(pending.Values.ToArray());
}
@@ -338,7 +336,6 @@ protected virtual void OnDisable()
_cached = Values;
_subscriptions.Clear();
_topicMap.Clear();
- _initialized = false;
}
protected virtual async void OnConnect()
@@ -353,23 +350,21 @@ private async Task RestartToComplete()
{
if (!RestartInProgress) return;
- _logger.Log("waiting for restart");
await restartTask.Task;
- _logger.Log("restart completed");
}
protected virtual void OnSubscribe(string id, PendingSubscription @params)
{
SetSubscription(id, new ActiveSubscription() { Id = id, Relay = @params.Relay, Topic = @params.Topic });
- pending.Remove(@params.Topic);
+ _ = pending.Remove(@params.Topic);
}
protected virtual void OnResubscribe(string id, PendingSubscription @params)
{
AddSubscription(id, new ActiveSubscription() { Id = id, Relay = @params.Relay, Topic = @params.Topic });
- pending.Remove(@params.Topic);
+ _ = pending.Remove(@params.Topic);
}
protected virtual async Task OnUnsubscribe(string topic, string id, Error reason)
@@ -445,10 +440,12 @@ protected virtual async Task UnsubscribeById(string topic, string id, Unsubscrib
protected virtual ActiveSubscription GetSubscription(string id)
{
- if (!_subscriptions.ContainsKey(id))
- throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY, Name + ": " + id);
+ if (!_subscriptions.TryGetValue(id, out var subscription))
+ {
+ throw new KeyNotFoundException($"No subscription found with id: {id}.");
+ }
- return _subscriptions[id];
+ return subscription;
}
protected virtual bool HasSubscription(string id, string topic)
@@ -471,7 +468,7 @@ protected virtual void IsInitialized()
{
if (!_initialized)
{
- throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, Name);
+ throw new InvalidOperationException($"{nameof(Subscriber)} module not initialized.");
}
}
@@ -554,23 +551,24 @@ public Task IsSubscribed(string topic)
protected virtual async Task RpcBatchSubscribe(string[] topics, ProtocolOptions relay)
{
- if (topics.Length == 0) return Array.Empty();
+ if (topics.Length == 0)
+ {
+ return [];
+ }
var api = RelayProtocols.GetRelayProtocol(relay.Protocol);
- var request = new RequestArguments()
+ var request = new RequestArguments
{
- Method = api.BatchSubscribe, Params = new BatchSubscribeParams() { Topics = topics }
+ Method = api.BatchSubscribe,
+ Params = new BatchSubscribeParams
+ {
+ Topics = topics
+ }
};
- try
- {
- return await this._relayer.Request(request)
- .WithTimeout(TimeSpan.FromSeconds(45));
- }
- catch (Exception e)
- {
- this._relayer.TriggerConnectionStalled();
- throw;
- }
+
+ return await _relayer
+ .Request(request)
+ .WithTimeout(TimeSpan.FromMinutes(1));
}
protected virtual async Task BatchSubscribe(PendingSubscription[] subscriptions)
@@ -578,9 +576,23 @@ protected virtual async Task BatchSubscribe(PendingSubscription[] subscriptions)
if (subscriptions.Length == 0) return;
var topics = subscriptions.Select(s => s.Topic).ToArray();
var relay = subscriptions[0].Relay;
- var result = await this.RpcBatchSubscribe(topics, relay);
+
+ string[] result;
+ try
+ {
+ result = await RpcBatchSubscribe(topics, relay);
+ }
+ catch (TimeoutException)
+ {
+ _relayer.TriggerConnectionStalled();
+ return;
+ }
+
OnBatchSubscribe(result
- .Select((r, i) => new ActiveSubscription() { Id = r, Relay = relay, Topic = topics[i] })
+ .Select((r, i) => new ActiveSubscription
+ {
+ Id = r, Relay = relay, Topic = topics[i]
+ })
.ToArray());
}
diff --git a/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs b/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs
index a5b9a7a..cf8ef1c 100644
--- a/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs
+++ b/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs
@@ -6,6 +6,7 @@
using WalletConnectSharp.Common.Utils;
using WalletConnectSharp.Core.Interfaces;
using WalletConnectSharp.Core.Models;
+using WalletConnectSharp.Core.Models.History;
using WalletConnectSharp.Core.Models.Relay;
using WalletConnectSharp.Crypto.Models;
using WalletConnectSharp.Network.Models;
@@ -123,6 +124,7 @@ async void RequestCallback(object sender, MessageEvent e)
}
catch (JsonException)
{
+ return;
}
}
@@ -153,7 +155,7 @@ async void ResponseCallback(object sender, MessageEvent e)
await responseCallback(topic, payload);
}
- catch (Exception ex) when (ex is JsonReaderException or JsonSerializationException)
+ catch (Exception ex) when (ex is JsonException)
{
if (!expectingResult)
return;
@@ -168,25 +170,25 @@ async void InspectResponseRaw(object sender, DecodedMessageEvent e)
var payload = e.Payload;
+ JsonRpcRecord record;
try
{
- var record = await rpcHistory.Get(topic, payload.Id);
-
- // ignored if we can't find anything in the history
- if (record == null) return;
- var resMethod = record.Request.Method;
-
- // Trigger the true response event, which will trigger ResponseCallback
- messageEventHandlerMap[$"response_{resMethod}"](this,
- new MessageEvent() { Topic = topic, Message = message });
+ record = await rpcHistory.Get(topic, payload.Id);
}
- catch (WalletConnectException err)
+ catch (KeyNotFoundException)
{
- if (err.CodeType != ErrorType.NO_MATCHING_KEY)
- throw;
-
- // ignored if we can't find anything in the history
+ // Ignored if we can't find anything in the history
+ return;
}
+
+ var resMethod = record.Request.Method;
+
+ // Trigger the true response event, which will trigger ResponseCallback
+ messageEventHandlerMap[$"response_{resMethod}"](this,
+ new MessageEvent
+ {
+ Topic = topic, Message = message
+ });
}
messageEventHandlerMap[$"request_{method}"] += RequestCallback;
@@ -213,7 +215,7 @@ async void InspectResponseRaw(object sender, DecodedMessageEvent e)
/// The second type to check for
/// constructed from the values found in the
/// from either type T1 or T2
- /// If no is found in either type
+ /// If no is found in either type
public PublishOptions RpcRequestOptionsFromType()
{
var opts = RpcRequestOptionsForType();
@@ -222,8 +224,10 @@ public PublishOptions RpcRequestOptionsFromType()
opts = RpcRequestOptionsForType();
if (opts == null)
{
- throw new Exception(
- $"No RpcRequestOptions attribute found in either {typeof(T1).FullName} or {typeof(T2).FullName}!");
+ throw new InvalidOperationException(
+ $"No RpcRequestOptions attribute found on either {typeof(T1).FullName} or {typeof(T2).FullName}. "
+ + $"Ensure that at least one of these types is decorated with the {nameof(RpcRequestOptionsAttribute)}."
+ );
}
}
@@ -237,18 +241,28 @@ public PublishOptions RpcRequestOptionsFromType()
/// The type to check for
/// constructed from the values found in the
/// from the given type T
- /// If no is found in the type T
+ /// If no is found in the type T or if multiple are found
public PublishOptions RpcRequestOptionsForType()
{
var attributes = typeof(T).GetCustomAttributes(typeof(RpcRequestOptionsAttribute), true);
- if (attributes.Length > 1)
- throw new Exception($"Type {typeof(T).FullName} has multiple RpcRequestOptions attributes!");
- if (attributes.Length == 0)
- return null;
+ switch (attributes.Length)
+ {
+ case 0:
+ throw new InvalidOperationException($"Type {typeof(T).FullName} has no {nameof(RpcRequestOptionsAttribute)} defined.");
+ case > 1:
+ throw new InvalidOperationException($"Type {typeof(T).FullName} has multiple {nameof(RpcRequestOptionsAttribute)} definitions. Only one is allowed.");
+ }
- var opts = attributes.Cast().First();
+ var opts = attributes.Cast().SingleOrDefault();
+ if (opts == null)
+ {
+ throw new InvalidOperationException($"Type {typeof(T).FullName} has multiple {nameof(RpcRequestOptionsAttribute)} definitions. Only one is allowed.");
+ }
- return new PublishOptions() { Tag = opts.Tag, TTL = opts.TTL };
+ return new PublishOptions
+ {
+ Tag = opts.Tag, TTL = opts.TTL
+ };
}
///
@@ -259,20 +273,15 @@ public PublishOptions RpcRequestOptionsForType()
/// The second type to check for
/// constructed from the values found in the
/// from either type T1 or T2
- /// If no is found in either type
+ /// If no is found in either type
public PublishOptions RpcResponseOptionsFromTypes()
{
- var opts = RpcResponseOptionsForType();
- if (opts != null)
- {
- return opts;
- }
-
- opts = RpcResponseOptionsForType();
+ var opts = RpcResponseOptionsForType() ?? RpcResponseOptionsForType();
if (opts == null)
{
- throw new Exception(
- $"No RpcResponseOptions attribute found in either {typeof(T1).FullName} or {typeof(T2).FullName}!");
+ throw new InvalidOperationException(
+ $"No {nameof(RpcResponseOptionsAttribute)} found on either {typeof(T1).FullName} or {typeof(T2).FullName}. " +
+ "Ensure that at least one of these types is decorated with the RpcResponseOptionsAttribute.");
}
return opts;
@@ -289,14 +298,24 @@ public PublishOptions RpcResponseOptionsFromTypes()
public PublishOptions RpcResponseOptionsForType()
{
var attributes = typeof(T).GetCustomAttributes(typeof(RpcResponseOptionsAttribute), true);
- if (attributes.Length > 1)
- throw new Exception($"Type {typeof(T).FullName} has multiple RpcResponseOptions attributes!");
- if (attributes.Length == 0)
- return null;
+ switch (attributes.Length)
+ {
+ case 0:
+ return null;
+ case > 1:
+ throw new InvalidOperationException($"Type {typeof(T).FullName} has multiple {nameof(RpcMethodAttribute)} definitions. Only one is allowed.");
+ }
- var opts = attributes.Cast().First();
+ var opts = attributes.Cast().SingleOrDefault();
+ if (opts == null)
+ {
+ throw new InvalidOperationException($"Type {typeof(T).FullName} has multiple {nameof(RpcMethodAttribute)} definitions. Only one is allowed.");
+ }
- return new PublishOptions() { Tag = opts.Tag, TTL = opts.TTL };
+ return new PublishOptions()
+ {
+ Tag = opts.Tag, TTL = opts.TTL
+ };
}
public void SetDecodeOptionsForTopic(DecodeOptions options, string topic)
diff --git a/WalletConnectSharp.Core/Models/Expirer/ExpirerTarget.cs b/WalletConnectSharp.Core/Models/Expirer/ExpirerTarget.cs
index feeb36e..ccbbdb8 100644
--- a/WalletConnectSharp.Core/Models/Expirer/ExpirerTarget.cs
+++ b/WalletConnectSharp.Core/Models/Expirer/ExpirerTarget.cs
@@ -30,31 +30,29 @@ public class ExpirerTarget
/// be converted and stored to either the ID field or Topic field
///
/// The to convert
- /// If the format for the given is invalid
+ /// If the format for the given is invalid
public ExpirerTarget(string target)
{
- var values = target.Split(":");
- var type = values[0];
- var value = values[1];
+ var values = target.Split(':');
+ if (values.Length != 2)
+ {
+ throw new FormatException($"Invalid target format: {target}. Expected format: 'type:value'.");
+ }
+
+ var (type, value) = (values[0], values[1]);
switch (type)
{
case "topic":
Topic = value;
break;
- case "id":
- {
- long id;
- var success = long.TryParse(value, out id);
-
- if (!success)
- throw new Exception($"Cannot parse id {value}");
-
+ case "id" when long.TryParse(value, out var id):
Id = id;
break;
- }
+ case "id":
+ throw new FormatException($"Cannot parse id {value} as a long.");
default:
- throw new Exception($"Invalid target, expected id:number or topic:string, got {type}:{value}");
+ throw new FormatException($"Invalid target type: {type}. Expected 'id' or 'topic'.");
}
}
}
diff --git a/WalletConnectSharp.Core/Models/Verify/Verifier.cs b/WalletConnectSharp.Core/Models/Verify/Verifier.cs
index 4d4d84b..02736f2 100644
--- a/WalletConnectSharp.Core/Models/Verify/Verifier.cs
+++ b/WalletConnectSharp.Core/Models/Verify/Verifier.cs
@@ -17,9 +17,7 @@ public async Task Resolve(string attestationId)
try
{
var url = $"{VerifyServer}/attestation/{attestationId}";
- WCLogger.Log($"[Verifier] Resolving attestation {attestationId} from {url}");
var results = await _client.GetStringAsync(url);
- WCLogger.Log($"[Verifier] Resolved attestation. Results: {results}");
var verifiedContext = JsonConvert.DeserializeObject(results);
diff --git a/WalletConnectSharp.Core/Utils.cs b/WalletConnectSharp.Core/Utils.cs
index f8804b8..946647e 100644
--- a/WalletConnectSharp.Core/Utils.cs
+++ b/WalletConnectSharp.Core/Utils.cs
@@ -27,6 +27,23 @@ public static bool IsValidChainId(string chainId)
return SessionIdRegex.IsMatch(chainId);
}
+ public static bool IsValidAccountId(string account)
+ {
+ if (string.IsNullOrWhiteSpace(account) || !account.Contains(':'))
+ {
+ return false;
+ }
+
+ var split = account.Split(":");
+ if (split.Length != 3)
+ {
+ return false;
+ }
+
+ var chainId = split[0] + ":" + split[1];
+ return !string.IsNullOrWhiteSpace(split[2]) && IsValidChainId(chainId);
+ }
+
public static bool IsValidRequestExpiry(long expiry, long min, long max)
{
return expiry <= max && expiry >= min;
diff --git a/WalletConnectSharp.Sign/Engine.cs b/WalletConnectSharp.Sign/Engine.cs
index deb2928..56dd1ca 100644
--- a/WalletConnectSharp.Sign/Engine.cs
+++ b/WalletConnectSharp.Sign/Engine.cs
@@ -358,9 +358,9 @@ public Task Disconnect(Error reason = null)
/// parsed from the given uri
public UriParameters ParseUri(string uri)
{
- var pathStart = uri.IndexOf(":", StringComparison.Ordinal);
- int? pathEnd = uri.IndexOf("?", StringComparison.Ordinal) != -1
- ? uri.IndexOf("?", StringComparison.Ordinal)
+ var pathStart = uri.IndexOf(':');
+ var pathEnd = uri.IndexOf('?') != -1
+ ? uri.IndexOf('?')
: (int?)null;
var protocol = uri.Substring(0, pathStart);
@@ -370,8 +370,7 @@ public UriParameters ParseUri(string uri)
var requiredValues = path.Split("@");
string queryString = pathEnd != null ? uri.Substring((int)pathEnd) : "";
- var queryParams = Regex.Matches(queryString, "([^?=&]+)(=([^&]*))?").Cast()
- .ToDictionary(x => x.Groups[1].Value, x => x.Groups[3].Value);
+ var queryParams = UrlUtils.ParseQs(queryString);
var result = new UriParameters()
{
@@ -452,7 +451,7 @@ public async Task Connect(ConnectOptions options)
if (string.IsNullOrWhiteSpace(topic))
{
- throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY, $"connect() pairing topic: {topic}");
+ throw new InvalidOperationException("The pairing topic is empty");
}
var id = await MessageHandler.SendRequest(topic, proposal);
@@ -543,10 +542,14 @@ public async Task Pair(string uri)
return;
if (args.VerifiedContext.Validation == Validation.Invalid)
+ {
sessionProposeTask.SetException(new Exception(
$"Could not validate, invalid validation status {args.VerifiedContext.Validation} for origin {args.VerifiedContext.Origin}"));
+ }
else
+ {
sessionProposeTask.SetResult(proposal);
+ }
},
h => Client.SessionProposed += h,
h => Client.SessionProposed -= h
@@ -852,7 +855,7 @@ public async Task Ping(string topic)
///
/// The topic of the session to disconnect
/// An (optional) error reason for the disconnect
- public async Task Disconnect(string topic, Error reason)
+ public async Task Disconnect(string topic, Error reason = null)
{
IsInitialized();
var error = reason ?? Error.FromErrorType(ErrorType.USER_DISCONNECTED);
diff --git a/WalletConnectSharp.Sign/Interfaces/IEnginePrivate.cs b/WalletConnectSharp.Sign/Interfaces/IEnginePrivate.cs
index 1f85e8d..f75023f 100644
--- a/WalletConnectSharp.Sign/Interfaces/IEnginePrivate.cs
+++ b/WalletConnectSharp.Sign/Interfaces/IEnginePrivate.cs
@@ -45,8 +45,6 @@ public interface IEnginePrivate
internal Task IsValidConnect(ConnectOptions options);
- internal Task IsValidPair(string uri);
-
internal Task IsValidSessionSettleRequest(SessionSettle settle);
internal Task IsValidApprove(ApproveParams @params);
diff --git a/WalletConnectSharp.Sign/Internals/EngineHandler.cs b/WalletConnectSharp.Sign/Internals/EngineHandler.cs
index a437ac1..ef3475e 100644
--- a/WalletConnectSharp.Sign/Internals/EngineHandler.cs
+++ b/WalletConnectSharp.Sign/Internals/EngineHandler.cs
@@ -21,7 +21,7 @@ async void ExpiredCallback(object sender, ExpirerEventArgs e)
if (target.Id != null && this.Client.PendingRequests.Keys.Contains((long)target.Id))
{
await PrivateThis.DeletePendingSessionRequest((long)target.Id,
- Error.FromErrorType(ErrorType.EXPIRED), true);
+ Error.FromErrorType(ErrorType.SESSION_REQUEST_EXPIRED), true);
return;
}
diff --git a/WalletConnectSharp.Sign/Internals/EngineValidation.cs b/WalletConnectSharp.Sign/Internals/EngineValidation.cs
index dcc9bbe..800757d 100644
--- a/WalletConnectSharp.Sign/Internals/EngineValidation.cs
+++ b/WalletConnectSharp.Sign/Internals/EngineValidation.cs
@@ -2,6 +2,7 @@
using WalletConnectSharp.Common.Model.Errors;
using WalletConnectSharp.Common.Utils;
using WalletConnectSharp.Core;
+using WalletConnectSharp.Core.Models.Relay;
using WalletConnectSharp.Network.Models;
using WalletConnectSharp.Sign.Interfaces;
using WalletConnectSharp.Sign.Models;
@@ -17,19 +18,16 @@ private void IsInitialized()
{
if (!_initialized)
{
- throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, Name);
+ throw new InvalidOperationException($"{nameof(Engine)} module not initialized.");
}
}
async Task IEnginePrivate.IsValidConnect(ConnectOptions options)
{
if (options == null)
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID, $"Connect() params: {JsonConvert.SerializeObject(options)}");
+ throw new ArgumentNullException(nameof(options));
var pairingTopic = options.PairingTopic;
- var requiredNamespaces = options.RequiredNamespaces;
- var relays = options.Relays;
-
if (pairingTopic != null)
await IsValidPairingTopic(pairingTopic);
}
@@ -37,34 +35,30 @@ async Task IEnginePrivate.IsValidConnect(ConnectOptions options)
async Task IsValidPairingTopic(string topic)
{
if (string.IsNullOrWhiteSpace(topic))
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID,
- $"pairing topic should be a string {topic}");
+ throw new ArgumentNullException(nameof(topic), "Pairing topic should be a valid string.");
if (!this.Client.Core.Pairing.Store.Keys.Contains(topic))
- throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY,
- $"pairing topic doesn't exist {topic}");
+ throw new KeyNotFoundException($"Paring topic {topic} doesn't exist in the pairing store.");
var expiry = this.Client.Core.Pairing.Store.Get(topic).Expiry;
if (expiry != null && Clock.IsExpired(expiry.Value))
{
- throw WalletConnectException.FromType(ErrorType.EXPIRED, $"pairing topic: {topic}");
+ throw new ExpiredException($"Pairing topic {topic} has expired.");
}
}
Task IsValidSessionTopic(string topic)
{
if (string.IsNullOrWhiteSpace(topic))
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID,
- $"session topic should be a string {topic}");
+ throw new ArgumentNullException(nameof(topic), "Session topic should be a valid string.");
if (!this.Client.Session.Keys.Contains(topic))
- throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY,
- $"session topic doesn't exist {topic}");
+ throw new KeyNotFoundException($"Session topic {topic} doesn't exist in the session store.");
var expiry = this.Client.Session.Get(topic).Expiry;
if (expiry != null && Clock.IsExpired(expiry.Value))
{
- throw WalletConnectException.FromType(ErrorType.EXPIRED, $"session topic: {topic}");
+ throw new ExpiredException($"Session topic {topic} has expired.");
}
return Task.CompletedTask;
@@ -73,75 +67,91 @@ Task IsValidSessionTopic(string topic)
async Task IsValidProposalId(long id)
{
if (!this.Client.Proposal.Keys.Contains(id))
- throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY,
- $"proposal id doesn't exist {id}");
+ throw new KeyNotFoundException($"Proposal id {id} doesn't exist in the proposal store.");
var expiry = this.Client.Proposal.Get(id).Expiry;
if (expiry != null && Clock.IsExpired(expiry.Value))
{
await PrivateThis.DeleteProposal(id);
- throw WalletConnectException.FromType(ErrorType.EXPIRED, $"proposal id: {id}");
+ throw new ExpiredException($"Proposal with id {id} has expired.");
}
}
- async Task IsValidSessionOrPairingTopic(string topic)
+ private async Task ValidateSessionOrPairingTopic(string topic)
{
- if (this.Client.Session.Keys.Contains(topic)) await this.IsValidSessionTopic(topic);
- else if (this.Client.Core.Pairing.Store.Keys.Contains(topic)) await this.IsValidPairingTopic(topic);
- else if (string.IsNullOrWhiteSpace(topic))
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID,
- $"session or pairing topic should be a string {topic}");
- else
+ if (string.IsNullOrWhiteSpace(topic))
{
- throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY,
- $"session or pairing topic doesn't exist {topic}");
+ throw new ArgumentNullException(nameof(topic), "Session or pairing topic should be a valid string.");
}
- }
- Task IEnginePrivate.IsValidPair(string uri)
- {
- if (!Utils.IsValidUrl(uri))
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID, $"pair() uri: {uri}");
- return Task.CompletedTask;
+ if (Client.Session.Keys.Contains(topic))
+ {
+ await IsValidSessionTopic(topic);
+ }
+ else if (Client.Core.Pairing.Store.Keys.Contains(topic))
+ {
+ await IsValidPairingTopic(topic);
+ }
+ else
+ {
+ throw new KeyNotFoundException($"Session or pairing topic doesn't exist. Topic value: {topic}.");
+ }
}
Task IEnginePrivate.IsValidSessionSettleRequest(SessionSettle settle)
{
if (settle == null)
{
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID, $"onSessionSettleRequest() params: {settle}");
+ throw new ArgumentNullException(nameof(settle));
}
var relay = settle.Relay;
var controller = settle.Controller;
var namespaces = settle.Namespaces;
var expiry = settle.Expiry;
- if (relay != null && string.IsNullOrWhiteSpace(relay.Protocol))
+
+ ValidateSessionSettleRelay(relay);
+ ValidateSessionSettleController(controller);
+ ValidateSessionSettleNamespaces(namespaces);
+ ValidateSessionSettleExpiry(expiry);
+
+ return Task.CompletedTask;
+
+ void ValidateSessionSettleRelay(ProtocolOptions relayToValidate)
{
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID,
- $"OnSessionSettleRequest() relay protocol should be a string");
+ if (relayToValidate != null && string.IsNullOrWhiteSpace(relayToValidate.Protocol))
+ {
+ throw new ArgumentException("Relay protocol should be a non-empty string.");
+ }
}
- if (string.IsNullOrWhiteSpace(controller.PublicKey))
+ void ValidateSessionSettleController(Participant controllerToValidate)
{
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID,
- "OnSessionSettleRequest controller public key should be a string");
+ if (string.IsNullOrWhiteSpace(controllerToValidate?.PublicKey))
+ {
+ throw new ArgumentException("Controller public key should be a non-empty string.");
+ }
}
- var validNamespacesError = IsValidNamespaces(namespaces, "OnSessionSettleRequest()");
- if (validNamespacesError != null)
- throw validNamespacesError.ToException();
+ void ValidateSessionSettleNamespaces(Namespaces namespacesToValidate)
+ {
+ ValidateNamespaces(namespacesToValidate, "OnSessionSettleRequest()");
+ }
- if (Clock.IsExpired(expiry))
- throw WalletConnectException.FromType(ErrorType.EXPIRED, "OnSessionSettleRequest()");
- return Task.CompletedTask;
+ void ValidateSessionSettleExpiry(long expiryToValidate)
+ {
+ if (Clock.IsExpired(expiryToValidate))
+ {
+ throw new InvalidOperationException("SessionSettleRequest has expired.");
+ }
+ }
}
async Task IEnginePrivate.IsValidApprove(ApproveParams @params)
{
if (@params == null)
{
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID, $"approve() params: {@params}");
+ throw new ArgumentNullException(nameof(@params));
}
var id = @params.Id;
@@ -152,24 +162,19 @@ async Task IEnginePrivate.IsValidApprove(ApproveParams @params)
await IsValidProposalId(id);
var proposal = this.Client.Proposal.Get(id);
- var validNamespacesError = IsValidNamespaces(namespaces, "approve()");
- if (validNamespacesError != null)
- throw validNamespacesError.ToException();
-
- var conformingNamespacesError = IsConformingNamespaces(proposal.RequiredNamespaces, namespaces, "update()");
- if (conformingNamespacesError != null)
- throw conformingNamespacesError.ToException();
+ ValidateNamespaces(namespaces, "approve()");
+ ValidateConformingNamespaces(proposal.RequiredNamespaces, namespaces, "update()");
if (relayProtocol != null && string.IsNullOrWhiteSpace(relayProtocol))
{
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID, $"approve() relayProtocol: {relayProtocol}");
+ throw new ArgumentException("RelayProtocol should be a non-empty string.");
}
- if (@params.SessionProperties != null && @params.SessionProperties.Values.Any(string.IsNullOrWhiteSpace))
+ if (@params.SessionProperties != null && properties.Values.Any(string.IsNullOrWhiteSpace))
{
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID,
- $"sessionProperties must be in Dictionary format with no null or " +
- $"empty/whitespace values. Received: {JsonConvert.SerializeObject(@params.SessionProperties)}");
+ throw new ArgumentException($"SessionProperties must be in Dictionary format with no null or empty/whitespace values. "
+ + $"Received: {JsonConvert.SerializeObject(@params.SessionProperties)}"
+ );
}
}
@@ -177,7 +182,7 @@ async Task IEnginePrivate.IsValidReject(RejectParams @params)
{
if (@params == null)
{
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID, $"reject() params: {@params}");
+ throw new ArgumentNullException(nameof(@params));
}
var id = @params.Id;
@@ -187,8 +192,7 @@ async Task IEnginePrivate.IsValidReject(RejectParams @params)
if (reason == null || string.IsNullOrWhiteSpace(reason.Message))
{
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID,
- $"reject() reason: ${JsonConvert.SerializeObject(reason)}");
+ throw new ArgumentException("Reject reason should be a non-empty string.");
}
}
@@ -198,14 +202,8 @@ async Task IEnginePrivate.IsValidUpdate(string topic, Namespaces namespaces)
var session = this.Client.Session.Get(topic);
- var validNamespaceError = IsValidNamespaces(namespaces, "update()");
- if (validNamespaceError != null)
- throw validNamespaceError.ToException();
-
- var conformingNamespacesError = IsConformingNamespaces(session.RequiredNamespaces, namespaces, "update()");
-
- if (conformingNamespacesError != null)
- throw conformingNamespacesError.ToException();
+ ValidateNamespaces(namespaces, "update()");
+ ValidateConformingNamespaces(session.RequiredNamespaces, namespaces, "update()");
}
async Task IEnginePrivate.IsValidExtend(string topic)
@@ -217,39 +215,40 @@ async Task IEnginePrivate.IsValidRequest(string topic, JsonRpcRequest requ
{
await IsValidSessionTopic(topic);
- var session = this.Client.Session.Get(topic);
- var namespaces = session.Namespaces;
- if (!IsValidNamespacesChainId(namespaces, chainId))
- {
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID, $"request() chainId: {chainId}");
- }
-
if (request == null || string.IsNullOrWhiteSpace(request.Method))
{
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID,
- $"request() ${JsonConvert.SerializeObject(request)}");
+ throw new ArgumentException("Request or request method is null.", nameof(request));
}
+
+ var session = this.Client.Session.Get(topic);
+ var namespaces = session.Namespaces;
+ ValidateNamespacesChainId(namespaces, chainId);
var validMethods = GetNamespacesMethodsForChainId(namespaces, chainId);
if (!validMethods.Contains(request.Method))
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID,
- $"request() method: {request.Method}");
+ {
+ throw new NamespacesException($"Method {request.Method} not found in namespaces for chainId {chainId}.");
+ }
}
async Task IEnginePrivate.IsValidRespond(string topic, JsonRpcResponse response)
{
await IsValidSessionTopic(topic);
- if (response == null || (response.Result == null && response.Error == null))
+ if (response == null)
+ {
+ throw new ArgumentNullException(nameof(response));
+ }
+
+ if (Equals(response.Result, default(T)) && response.Error == null)
{
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID,
- $"respond() response: ${JsonConvert.SerializeObject(response)}");
+ throw new ArgumentException("Response result and error cannot both be null.");
}
}
async Task IEnginePrivate.IsValidPing(string topic)
{
- await IsValidSessionOrPairingTopic(topic);
+ await ValidateSessionOrPairingTopic(topic);
}
private List GetNamespacesEventsForChainId(Namespaces namespaces, string chainId)
@@ -267,101 +266,55 @@ private List GetNamespacesEventsForChainId(Namespaces namespaces, string
async Task IEnginePrivate.IsValidEmit(string topic, EventData eventData, string chainId)
{
await IsValidSessionTopic(topic);
- var session = this.Client.Session.Get(topic);
- var namespaces = session.Namespaces;
- if (!IsValidNamespacesChainId(namespaces, chainId))
+ if (eventData == null)
{
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID, $"emit() chainId: {chainId}");
+ throw new ArgumentNullException(nameof(eventData));
}
- if (eventData == null || string.IsNullOrWhiteSpace(eventData.Name))
+ if (string.IsNullOrWhiteSpace(eventData.Name))
{
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID, $"emit() event: {JsonConvert.SerializeObject(eventData)}");
+ throw new ArgumentException("Event name should be a non-empty string.");
}
+
+ var session = this.Client.Session.Get(topic);
+ var namespaces = session.Namespaces;
+
+ ValidateNamespacesChainId(namespaces, chainId);
if (!GetNamespacesEventsForChainId(namespaces, chainId).Contains(eventData.Name))
{
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID,
- $"emit() event: {JsonConvert.SerializeObject(eventData)}");
+ throw new NamespacesException($"Event {eventData.Name} not found in namespaces for chainId {chainId}.");
}
}
async Task IEnginePrivate.IsValidDisconnect(string topic, Error reason)
{
- if (string.IsNullOrWhiteSpace(topic))
- {
- throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID, $"disconnect() params: {topic}");
- }
-
- await IsValidSessionOrPairingTopic(topic);
+ await ValidateSessionOrPairingTopic(topic);
}
- private bool IsValidAccountId(string account)
+ private static void ValidateAccounts(string[] accounts, string context)
{
- if (!string.IsNullOrWhiteSpace(account) && account.Contains(":"))
- {
- var split = account.Split(":");
- if (split.Length == 3)
- {
- var chainId = split[0] + ":" + split[1];
- return !string.IsNullOrWhiteSpace(split[2]) && IsValidChainId(chainId);
- }
- }
- return false;
- }
-
- private Error IsValidAccounts(string[] accounts, string context)
- {
- Error error = null;
foreach (var account in accounts)
{
- if (error != null)
- break;
-
- if (!IsValidAccountId(account))
+ if (!Utils.IsValidAccountId(account))
{
- error = Error.FromErrorType(ErrorType.UNSUPPORTED_ACCOUNTS, $"{context}, account {account} should be a string and conform to 'namespace:chainId:address' format");
+ throw new FormatException($"{context}, account {account} should be a string and conform to 'namespace:chainId:address' format.");
}
}
-
- return error;
}
- private Error IsValidNamespaceAccounts(Namespaces namespaces, string method)
+ private static void ValidateNamespaces(Namespaces namespaces, string method)
{
- Error error = null;
- foreach (var ns in namespaces.Values)
+ if (namespaces == null)
{
- if (error != null) break;
-
- var validAccountsError = IsValidAccounts(ns.Accounts, $"{method} namespace");
- if (validAccountsError != null)
- {
- error = validAccountsError;
- }
+ throw new ArgumentNullException(nameof(namespaces));
}
- return error;
- }
-
- private Error IsValidNamespaces(Namespaces namespaces, string method)
- {
- Error error = null;
- if (namespaces != null)
- {
- var validAccountsError = IsValidNamespaceAccounts(namespaces, method);
- if (validAccountsError != null)
- {
- error = validAccountsError;
- }
- }
- else
+ foreach (var ns in namespaces.Values)
{
- error = Error.FromErrorType(ErrorType.MISSING_OR_INVALID, $"{method}, namespaces should be an object with data");
+ ValidateAccounts(ns.Accounts, $"{method} namespace");
}
-
- return error;
}
private List GetNamespacesMethodsForChainId(Namespaces namespaces, string chainId)
@@ -376,20 +329,9 @@ private List GetNamespacesMethodsForChainId(Namespaces namespaces, strin
return methods;
}
- private bool IsValidChainId(string chainId)
- {
- if (chainId.Contains(":"))
- {
- var split = chainId.Split(":");
- return split.Length == 2;
- }
-
- return false;
- }
-
private List GetNamespacesChains(Namespaces namespaces)
{
- List chains = new List();
+ List chains = [];
foreach (var ns in namespaces.Values)
{
chains.AddRange(GetAccountsChains(ns.Accounts));
@@ -398,50 +340,53 @@ private List GetNamespacesChains(Namespaces namespaces)
return chains;
}
- private bool IsValidNamespacesChainId(Namespaces namespaces, string chainId)
+ private void ValidateNamespacesChainId(Namespaces namespaces, string chainId)
{
- if (!IsValidChainId(chainId)) return false;
- var chains = GetNamespacesChains(namespaces);
- if (!chains.Contains(chainId)) return false;
+ if (!Utils.IsValidChainId(chainId))
+ {
+ throw new FormatException($"ChainId {chainId} should be a string and conform to 'chainId:chainId' format.");
+ }
- return true;
+ var chains = GetNamespacesChains(namespaces);
+ if (!chains.Contains(chainId))
+ {
+ throw new NamespacesException($"ChainId {chainId} is invalid or not found in namespaces.");
+ }
}
- private Error IsConformingNamespaces(RequiredNamespaces requiredNamespaces, Namespaces namespaces,
+ private void ValidateConformingNamespaces(
+ RequiredNamespaces requiredNamespaces,
+ Namespaces namespaces,
string context)
{
- Error error = null;
var requiredNamespaceKeys = requiredNamespaces.Keys.ToArray();
var namespaceKeys = namespaces.Keys.ToArray();
-
+
if (!HasOverlap(requiredNamespaceKeys, namespaceKeys))
- error = Error.FromErrorType(ErrorType.NON_CONFORMING_NAMESPACES, $"{context} namespaces keys don't satisfy requiredNamespaces");
- else
{
- foreach (var key in requiredNamespaceKeys)
+ throw new NamespacesException($"Namespaces keys don't satisfy requiredNamespaces, {context}.");
+ }
+
+ foreach (var key in requiredNamespaceKeys)
+ {
+ var requiredNamespaceChains = requiredNamespaces[key].Chains;
+ var namespaceChains = GetAccountsChains(namespaces[key].Accounts);
+
+ if (!HasOverlap(requiredNamespaceChains, namespaceChains))
{
- if (error != null)
- break;
+ throw new NamespacesException($"Namespaces chains don't satisfy requiredNamespaces chains for {key}, {context}.");
+ }
- var requiredNamespaceChains = requiredNamespaces[key].Chains;
- var namespaceChains = GetAccountsChains(namespaces[key].Accounts);
+ if (!HasOverlap(requiredNamespaces[key].Methods, namespaces[key].Methods))
+ {
+ throw new NamespacesException($"Namespaces methods don't satisfy requiredNamespaces methods for {key}, {context}.");
+ }
- if (!HasOverlap(requiredNamespaceChains, namespaceChains))
- {
- error = Error.FromErrorType(ErrorType.NON_CONFORMING_NAMESPACES, $"{context} namespaces accounts don't satisfy requiredNamespaces chains for {key}");
- }
- else if (!HasOverlap(requiredNamespaces[key].Methods, namespaces[key].Methods))
- {
- error = Error.FromErrorType(ErrorType.NON_CONFORMING_NAMESPACES, $"{context} namespaces methods don't satisfy requiredNamespaces methods for {key}");
- }
- else if (!HasOverlap(requiredNamespaces[key].Events, namespaces[key].Events))
- {
- error = Error.FromErrorType(ErrorType.NON_CONFORMING_NAMESPACES, $"{context} namespaces events don't satisfy requiredNamespaces events for {key}");
- }
+ if (!HasOverlap(requiredNamespaces[key].Events, namespaces[key].Events))
+ {
+ throw new NamespacesException($"Namespaces events don't satisfy requiredNamespaces events for {key}, {context}.");
}
}
-
- return error;
}
private bool HasOverlap(string[] a, string[] b)
@@ -450,9 +395,9 @@ private bool HasOverlap(string[] a, string[] b)
return matches.Count() == a.Length;
}
- private string[] GetAccountsChains(string[] accounts)
+ private static string[] GetAccountsChains(string[] accounts)
{
- List chains = new List();
+ List chains = [];
foreach (var account in accounts)
{
var values = account.Split(":");
@@ -474,21 +419,30 @@ private bool IsSessionCompatible(SessionStruct session, RequiredNamespaces requi
if (!HasOverlap(paramsKeys, sessionKeys)) return false;
- foreach (var key in sessionKeys)
+ try
{
- var value = session.Namespaces[key];
- var accounts = value.Accounts;
- var methods = value.Methods;
- var events = value.Events;
- var chains = GetAccountsChains(accounts);
- var requiredNamespace = requiredNamespaces[key];
-
- if (!HasOverlap(requiredNamespace.Chains, chains) ||
- !HasOverlap(requiredNamespace.Methods, methods) ||
- !HasOverlap(requiredNamespace.Events, events))
- compatible = false;
+ foreach (var key in sessionKeys)
+ {
+ var value = session.Namespaces[key];
+ var accounts = value.Accounts;
+ var methods = value.Methods;
+ var events = value.Events;
+ var chains = GetAccountsChains(accounts);
+ var requiredNamespace = requiredNamespaces[key];
+
+ if (!HasOverlap(requiredNamespace.Chains, chains) ||
+ !HasOverlap(requiredNamespace.Methods, methods) ||
+ !HasOverlap(requiredNamespace.Events, events))
+ {
+ compatible = false;
+ }
+ }
}
-
+ catch (KeyNotFoundException e)
+ {
+ return false;
+ }
+
return compatible;
}
}
diff --git a/WalletConnectSharp.Sign/Models/ProposalStruct.cs b/WalletConnectSharp.Sign/Models/ProposalStruct.cs
index 657e0cd..5a4e8ad 100644
--- a/WalletConnectSharp.Sign/Models/ProposalStruct.cs
+++ b/WalletConnectSharp.Sign/Models/ProposalStruct.cs
@@ -1,7 +1,3 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
using Newtonsoft.Json;
using WalletConnectSharp.Common.Model.Errors;
using WalletConnectSharp.Core.Interfaces;
@@ -29,14 +25,8 @@ public struct ProposalStruct : IKeyHolder
/// so this struct can be stored using
///
[JsonIgnore]
- public long Key
- {
- get
- {
- return (long) Id;
- }
- }
-
+ public long Key => Id;
+
///
/// When this proposal expires
///
@@ -106,14 +96,23 @@ public ApproveParams ApproveProposal(string approvedAccount, ProtocolOptions pro
/// (optional) The protocol option to use. If left null, then the first protocol
/// option in this proposal will be chosen.
/// The that can be given to
+ /// If this proposal has no Id
+ /// If the requested protocol option does not exist in this proposal
public ApproveParams ApproveProposal(string[] approvedAccounts, ProtocolOptions protocolOption = null)
{
- if (Id == null)
- throw new Exception("Proposal has no set Id");
+ if (Id == default)
+ {
+ throw new InvalidOperationException("Proposal has no Id.");
+ }
+
if (protocolOption == null)
+ {
protocolOption = Relays[0];
- else if (Relays.All(r => r.Protocol != protocolOption.Protocol))
- throw new Exception("Requested protocol not in proposal");
+ }
+ else if (Array.TrueForAll(Relays, r => r.Protocol != protocolOption.Protocol))
+ {
+ throw new InvalidOperationException("Requested protocol option does not exist in this proposal.");
+ }
var relayProtocol = protocolOption.Protocol;
@@ -164,13 +163,18 @@ public ApproveParams ApproveProposal(string[] approvedAccounts, ProtocolOptions
///
/// The error reason this proposal was rejected
/// A new object which must be used in
- /// If this proposal has no Id
+ /// If this proposal has no Id
public RejectParams RejectProposal(Error error)
{
- if (Id == null)
- throw new Exception("Proposal has no set Id");
+ if (Id == default)
+ {
+ throw new InvalidOperationException("Proposal has no Id.");
+ }
- return new RejectParams() {Id = Id, Reason = error};
+ return new RejectParams
+ {
+ Id = Id, Reason = error
+ };
}
///
diff --git a/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs b/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs
index 737b6c5..9975bac 100644
--- a/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs
+++ b/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs
@@ -200,7 +200,7 @@ private void IsInitialized()
{
if (!_initialized)
{
- throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, "Web3WalletEngine");
+ throw new InvalidOperationException($"{nameof(Web3WalletEngine)} module not initialized.");
}
}
@@ -210,25 +210,16 @@ private void IsInitialized()
void OnAuthRequest(object sender, AuthRequest request)
{
- if (AuthRequested != null)
- {
- AuthRequested(sender, request);
- }
+ AuthRequested?.Invoke(sender, request);
}
void OnAuthResponse(object sender, AuthErrorResponse errorResponse)
{
- if (AuthError != null)
- {
- AuthError(sender, errorResponse);
- }
+ AuthError?.Invoke(sender, errorResponse);
}
void OnAuthResponse(object sender, AuthResponse response)
{
- if (AuthResponded != null)
- {
- AuthResponded(sender, response);
- }
+ AuthResponded?.Invoke(sender, response);
}
}
diff --git a/WalletConnectSharpV2.sln.DotSettings b/WalletConnectSharpV2.sln.DotSettings
new file mode 100644
index 0000000..2da1370
--- /dev/null
+++ b/WalletConnectSharpV2.sln.DotSettings
@@ -0,0 +1,2 @@
+
+ True
\ No newline at end of file