From b5bb95816333f6b1b6a580862dd51a0374bfff34 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Sun, 7 May 2023 20:42:43 -0400 Subject: [PATCH 01/36] feat: init auth --- .../Internals/CryptoUtils.cs | 42 ++++++++++++++++++ .../Internals/IssDidUtils.cs | 44 +++++++++++++++++++ .../WalletConnectSharp.Auth.csproj | 36 +++++++++++++++ .../WalletConnectSharp.Core.csproj | 2 +- .../WalletConnectSharp.Sign.csproj | 2 +- WalletConnectSharpV2.sln | 6 +++ 6 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 WalletConnectSharp.Auth/Internals/CryptoUtils.cs create mode 100644 WalletConnectSharp.Auth/Internals/IssDidUtils.cs create mode 100644 WalletConnectSharp.Auth/WalletConnectSharp.Auth.csproj diff --git a/WalletConnectSharp.Auth/Internals/CryptoUtils.cs b/WalletConnectSharp.Auth/Internals/CryptoUtils.cs new file mode 100644 index 0000000..1fc755e --- /dev/null +++ b/WalletConnectSharp.Auth/Internals/CryptoUtils.cs @@ -0,0 +1,42 @@ +using System.Security.Cryptography; + +namespace WalletConnectSharp.Auth.Internals +{ + public class CryptoUtils + { + private static readonly char[] ALPHANUMERIC = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray(); + + public static string GenerateNonce() + { + int bits = 96; + int charsetLength = ALPHANUMERIC.Length; + int length = (int)Math.Ceiling(bits / (Math.Log10(charsetLength) / Math.Log(2))); + + string @out = ""; + int maxByte = 256 - (256 % charsetLength); + + using (var rng = RandomNumberGenerator.Create()) + { + while (length > 0) + { + byte[] buf = new byte[(int)Math.Ceiling(length * 256.0 / maxByte)]; + + rng.GetBytes(buf); + + for (int i = 0; i < buf.Length && length > 0; i++) + { + var randomByte = buf[i]; + if (randomByte < maxByte) + { + @out += ALPHANUMERIC[randomByte % charsetLength]; + length--; + } + } + } + } + + return @out; + } + } +} diff --git a/WalletConnectSharp.Auth/Internals/IssDidUtils.cs b/WalletConnectSharp.Auth/Internals/IssDidUtils.cs new file mode 100644 index 0000000..f76e824 --- /dev/null +++ b/WalletConnectSharp.Auth/Internals/IssDidUtils.cs @@ -0,0 +1,44 @@ +namespace WalletConnectSharp.Auth.Internals +{ + public static class IssDidUtils + { + public static string[] ExtractDidAddressSegments(string iss) + { + if (string.IsNullOrWhiteSpace(iss)) + return null; + + return iss.Split(":"); + } + + public static string DidChainId(string iss) + { + if (string.IsNullOrWhiteSpace(iss)) + return null; + + return ExtractDidAddressSegments(iss)[3]; + } + + public static string NamespacedDidChainId(string iss) + { + if (string.IsNullOrWhiteSpace(iss)) + return null; + + var segments = ExtractDidAddressSegments(iss); + + return $"{segments[2]}:{segments[3]}"; + } + + public static string DidAddress(string iss) + { + if (string.IsNullOrWhiteSpace(iss)) + return null; + + var segments = ExtractDidAddressSegments(iss); + + if (segments.Length == 0) + return null; + + return segments[segments.Length - 1]; + } + } +} diff --git a/WalletConnectSharp.Auth/WalletConnectSharp.Auth.csproj b/WalletConnectSharp.Auth/WalletConnectSharp.Auth.csproj new file mode 100644 index 0000000..6d7bad0 --- /dev/null +++ b/WalletConnectSharp.Auth/WalletConnectSharp.Auth.csproj @@ -0,0 +1,36 @@ + + + + $(DefaultTargetFrameworks) + $(DefaultVersion) + $(DefaultVersion) + $(DefaultVersion) + WalletConnect.Auth + WalletConnectSharp.Auth + pedrouid, gigajuwels + A port of the TypeScript SDK to C#. A complete implementation of the WalletConnect v2 protocol that can be used to connect to external wallets or connect a wallet to an external Dapp + Copyright (c) WalletConnect 2023 + https://walletconnect.org/ + icon.png + https://github.com/WalletConnect/WalletConnectSharp + git + walletconnect sign wallet web3 ether ethereum blockchain evm + true + Apache-2.0 + + + + + + + + + + + + + + + + + diff --git a/WalletConnectSharp.Core/WalletConnectSharp.Core.csproj b/WalletConnectSharp.Core/WalletConnectSharp.Core.csproj index b7aee2d..59697c5 100644 --- a/WalletConnectSharp.Core/WalletConnectSharp.Core.csproj +++ b/WalletConnectSharp.Core/WalletConnectSharp.Core.csproj @@ -7,7 +7,7 @@ $(DefaultVersion) WalletConnect.Core WalletConnectSharp.Core - pedrouid, gigajuwels, edkek + pedrouid, gigajuwels A port of the TypeScript SDK to C#. A complete implementation of the WalletConnect v2 protocol that can be used to connect to external wallets or connect a wallet to an external Dapp Copyright (c) WalletConnect 2023 https://walletconnect.org/ diff --git a/WalletConnectSharp.Sign/WalletConnectSharp.Sign.csproj b/WalletConnectSharp.Sign/WalletConnectSharp.Sign.csproj index 0cf0890..8983f8c 100644 --- a/WalletConnectSharp.Sign/WalletConnectSharp.Sign.csproj +++ b/WalletConnectSharp.Sign/WalletConnectSharp.Sign.csproj @@ -7,7 +7,7 @@ $(DefaultVersion) WalletConnect.Sign WalletConnectSharp.Sign - pedrouid, gigajuwels, edkek + pedrouid, gigajuwels A port of the TypeScript SDK to C#. A complete implementation of the WalletConnect v2 protocol that can be used to connect to external wallets or connect a wallet to an external Dapp Copyright (c) WalletConnect 2023 https://walletconnect.org/ diff --git a/WalletConnectSharpV2.sln b/WalletConnectSharpV2.sln index e645502..2a98ab7 100644 --- a/WalletConnectSharpV2.sln +++ b/WalletConnectSharpV2.sln @@ -34,6 +34,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WalletConnectSharp.Sign.Tes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WalletConnectSharp.Tests.Common", "Tests\WalletConnectSharp.Tests.Common\WalletConnectSharp.Tests.Common.csproj", "{C3CC2BC9-CD29-46B8-94DB-B104E427330B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WalletConnectSharp.Auth", "WalletConnectSharp.Auth\WalletConnectSharp.Auth.csproj", "{5189138F-FAE5-4B2C-912F-CEB429C156AC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -100,6 +102,10 @@ Global {C3CC2BC9-CD29-46B8-94DB-B104E427330B}.Debug|Any CPU.Build.0 = Debug|Any CPU {C3CC2BC9-CD29-46B8-94DB-B104E427330B}.Release|Any CPU.ActiveCfg = Release|Any CPU {C3CC2BC9-CD29-46B8-94DB-B104E427330B}.Release|Any CPU.Build.0 = Release|Any CPU + {5189138F-FAE5-4B2C-912F-CEB429C156AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5189138F-FAE5-4B2C-912F-CEB429C156AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5189138F-FAE5-4B2C-912F-CEB429C156AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5189138F-FAE5-4B2C-912F-CEB429C156AC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {23897558-4177-425D-B2FA-75AB0B6FEDAE} = {3A55D6C7-0EF8-4EEA-90A5-89F5AFB97BA0} From 7cb7049c50a62648a4f6b2c363ff4567264ea172 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Sat, 3 Jun 2023 01:41:39 -0400 Subject: [PATCH 02/36] feat: complete auth client --- .../WalletConnectSharp.Common/Utils/Clock.cs | 7 +- .../WalletConnectSharp.Crypto/Crypto.cs | 12 +- .../Interfaces/ICrypto.cs | 8 + WalletConnectSharp.Auth/AuthClient.cs | 148 +++++++ WalletConnectSharp.Auth/Cacao.cs | 126 ++++++ .../Controllers/AuthEngine.cs | 372 ++++++++++++++++++ .../Interfaces/IAuthClient.cs | 41 ++ .../Interfaces/IAuthEngine.cs | 19 + .../Internals/AuthEngineValidations.cs | 47 +++ .../Internals/IssDidUtils.cs | 9 +- .../Internals/SignatureUtils.cs | 76 ++++ WalletConnectSharp.Auth/Models/AuthData.cs | 21 + .../Models/AuthErrorResponse.cs | 10 + WalletConnectSharp.Auth/Models/AuthOptions.cs | 11 + WalletConnectSharp.Auth/Models/AuthPayload.cs | 37 ++ WalletConnectSharp.Auth/Models/AuthRequest.cs | 13 + .../Models/AuthRequestData.cs | 13 + .../Models/AuthResponse.cs | 10 + .../Models/ErrorResponse.cs | 9 + WalletConnectSharp.Auth/Models/Message.cs | 20 + WalletConnectSharp.Auth/Models/Metadata.cs | 24 ++ WalletConnectSharp.Auth/Models/PairingData.cs | 21 + .../Models/PayloadParams.cs | 12 + .../Models/PendingRequest.cs | 15 + .../Models/RedirectData.cs | 12 + .../Models/RequestParams.cs | 9 + WalletConnectSharp.Auth/Models/RequestUri.cs | 12 + WalletConnectSharp.Auth/Models/Requester.cs | 12 + .../Models/ResultResponse.cs | 9 + .../Models/TopicMessage.cs | 9 + .../WalletConnectSharp.Auth.csproj | 6 - WalletConnectSharp.Auth/WcAuthRequest.cs | 17 + .../Controllers/TypedMessageHandler.cs | 13 +- WalletConnectSharp.Core/Core.cs | 8 + WalletConnectSharp.Core/Interfaces/ICore.cs | 3 + .../Interfaces/ITypedMessageHandler.cs | 10 +- WalletConnectSharp.Core/Models/Eth/EthCall.cs | 12 + .../Models/Verify/Validation.cs | 8 + .../Models/Verify/VerifiedContext.cs | 31 ++ .../Models/Verify/Verifier.cs | 28 ++ WalletConnectSharp.Core/Utils.cs | 24 ++ .../Internals/EngineValidation.cs | 18 +- 42 files changed, 1286 insertions(+), 36 deletions(-) create mode 100644 WalletConnectSharp.Auth/AuthClient.cs create mode 100644 WalletConnectSharp.Auth/Cacao.cs create mode 100644 WalletConnectSharp.Auth/Controllers/AuthEngine.cs create mode 100644 WalletConnectSharp.Auth/Interfaces/IAuthClient.cs create mode 100644 WalletConnectSharp.Auth/Interfaces/IAuthEngine.cs create mode 100644 WalletConnectSharp.Auth/Internals/AuthEngineValidations.cs create mode 100644 WalletConnectSharp.Auth/Internals/SignatureUtils.cs create mode 100644 WalletConnectSharp.Auth/Models/AuthData.cs create mode 100644 WalletConnectSharp.Auth/Models/AuthErrorResponse.cs create mode 100644 WalletConnectSharp.Auth/Models/AuthOptions.cs create mode 100644 WalletConnectSharp.Auth/Models/AuthPayload.cs create mode 100644 WalletConnectSharp.Auth/Models/AuthRequest.cs create mode 100644 WalletConnectSharp.Auth/Models/AuthRequestData.cs create mode 100644 WalletConnectSharp.Auth/Models/AuthResponse.cs create mode 100644 WalletConnectSharp.Auth/Models/ErrorResponse.cs create mode 100644 WalletConnectSharp.Auth/Models/Message.cs create mode 100644 WalletConnectSharp.Auth/Models/Metadata.cs create mode 100644 WalletConnectSharp.Auth/Models/PairingData.cs create mode 100644 WalletConnectSharp.Auth/Models/PayloadParams.cs create mode 100644 WalletConnectSharp.Auth/Models/PendingRequest.cs create mode 100644 WalletConnectSharp.Auth/Models/RedirectData.cs create mode 100644 WalletConnectSharp.Auth/Models/RequestParams.cs create mode 100644 WalletConnectSharp.Auth/Models/RequestUri.cs create mode 100644 WalletConnectSharp.Auth/Models/Requester.cs create mode 100644 WalletConnectSharp.Auth/Models/ResultResponse.cs create mode 100644 WalletConnectSharp.Auth/Models/TopicMessage.cs create mode 100644 WalletConnectSharp.Auth/WcAuthRequest.cs create mode 100644 WalletConnectSharp.Core/Models/Eth/EthCall.cs create mode 100644 WalletConnectSharp.Core/Models/Verify/Validation.cs create mode 100644 WalletConnectSharp.Core/Models/Verify/VerifiedContext.cs create mode 100644 WalletConnectSharp.Core/Models/Verify/Verifier.cs create mode 100644 WalletConnectSharp.Core/Utils.cs diff --git a/Core Modules/WalletConnectSharp.Common/Utils/Clock.cs b/Core Modules/WalletConnectSharp.Common/Utils/Clock.cs index 0393a92..28cea57 100644 --- a/Core Modules/WalletConnectSharp.Common/Utils/Clock.cs +++ b/Core Modules/WalletConnectSharp.Common/Utils/Clock.cs @@ -164,5 +164,10 @@ public static long Now() { return ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds(); } + + public static TimeSpan AsTimeSpan(long seconds) + { + return TimeSpan.FromSeconds(seconds); + } } -} \ No newline at end of file +} diff --git a/Core Modules/WalletConnectSharp.Crypto/Crypto.cs b/Core Modules/WalletConnectSharp.Crypto/Crypto.cs index 395bae5..5ba03dc 100644 --- a/Core Modules/WalletConnectSharp.Crypto/Crypto.cs +++ b/Core Modules/WalletConnectSharp.Crypto/Crypto.cs @@ -43,8 +43,8 @@ public class Crypto : ICrypto private static readonly Encoding DATA_ENCODING = Encoding.UTF8; private static readonly Encoding JSON_ENCODING = Encoding.UTF8; - private const int TYPE_0 = 0; - private const int TYPE_1 = 1; + public const int TYPE_0 = 0; + public const int TYPE_1 = 1; private const int TYPE_LENGTH = 1; private const int IV_LENGTH = 12; private const int KEY_LENGTH = 32; @@ -582,7 +582,13 @@ private void IsInitialized() } } - private string HashKey(string key) + /// + /// Hash a hex key string using SHA256. The input key string must be a hex + /// string and the returned hash is represented as a hex string + /// + /// The input hex key string to hash using SHA256 + /// The hash of the given input as a hex string + public string HashKey(string key) { using (SHA256 sha256 = SHA256.Create()) { diff --git a/Core Modules/WalletConnectSharp.Crypto/Interfaces/ICrypto.cs b/Core Modules/WalletConnectSharp.Crypto/Interfaces/ICrypto.cs index 32f5ea8..5ed84f4 100644 --- a/Core Modules/WalletConnectSharp.Crypto/Interfaces/ICrypto.cs +++ b/Core Modules/WalletConnectSharp.Crypto/Interfaces/ICrypto.cs @@ -121,5 +121,13 @@ public interface ICrypto : IModule /// /// The client id as a string Task GetClientId(); + + /// + /// Hash a hex key string using SHA256. The input key string must be a hex + /// string and the returned hash is represented as a hex string + /// + /// The input hex key string to hash using SHA256 + /// The hash of the given input as a hex string + string HashKey(string key); } } diff --git a/WalletConnectSharp.Auth/AuthClient.cs b/WalletConnectSharp.Auth/AuthClient.cs new file mode 100644 index 0000000..8f7bdbe --- /dev/null +++ b/WalletConnectSharp.Auth/AuthClient.cs @@ -0,0 +1,148 @@ +using WalletConnectSharp.Auth.Controllers; +using WalletConnectSharp.Auth.Interfaces; +using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Core.Controllers; +using WalletConnectSharp.Core.Interfaces; +using WalletConnectSharp.Events; + +namespace WalletConnectSharp.Auth; + +public class AuthClient : IAuthClient +{ + public const string AUTH_CLIENT_PROTOCOL = AuthEngine.AUTH_CLIENT_PROTOCOL; + public const int AUTH_CLIENT_VERSION = AuthEngine.AUTH_CLIENT_VERSION; + public const string AUTH_CLIENT_DEFAULT_NAME = "authClient"; + public static readonly string AUTH_CLIENT_STORAGE_PREFIX = AuthEngine.AUTH_CLIENT_STORAGE_PREFIX; + + public string Name + { + get + { + return AUTH_CLIENT_DEFAULT_NAME; + } + } + + public string Context + { + get + { + return $"{Name}-{Version}-context"; + } + } + + public EventDelegator Events { get; } + + public string Protocol + { + get + { + return AUTH_CLIENT_PROTOCOL; + } + } + + public int Version + { + get + { + return AUTH_CLIENT_VERSION; + } + } + + public event EventHandler AuthRequested; + public event EventHandler AuthResponded; + public event EventHandler AuthError; + public ICore Core { get; set; } + public Metadata Metadata { get; set; } + public string ProjectId { get; set; } + public IStore AuthKeys { get; set; } + public IStore PairingTopics { get; set; } + public IStore Requests { get; set; } + public IAuthEngine Engine { get; } + public AuthOptions Options { get; } + + public IDictionary PendingRequests + { + get + { + return Engine.PendingRequests; + } + } + + public static async Task Init(AuthOptions options) + { + var client = new AuthClient(options); + await client.Initialize(); + return client; + } + + private async Task Initialize() + { + await this.Core.Start(); + await this.AuthKeys.Init(); + await this.Requests.Init(); + await this.PairingTopics.Init(); + this.Engine.Init(); + } + + private AuthClient(AuthOptions options) + { + Options = options; + Metadata = options.Metadata; + ProjectId = options.ProjectId; + Core = options.Core ?? new Core.Core(options); + + AuthKeys = new Store(Core, "authKeys", AUTH_CLIENT_STORAGE_PREFIX); + PairingTopics = new Store(Core, "pairingTopics", AUTH_CLIENT_STORAGE_PREFIX); + Requests = new Store(Core, "requests", AUTH_CLIENT_STORAGE_PREFIX); + + Engine = new AuthEngine(this); + } + + public Task Request(RequestParams @params, string topic = null) + { + return this.Engine.Request(@params, topic); + } + + public Task Respond(Message message, string iss) + { + return this.Engine.Respond(message, iss); + } + + public string FormatMessage(Cacao.CacaoPayload cacao, string iss) + { + return this.Engine.FormatMessage(cacao, iss); + } + + bool IAuthClient.OnAuthRequest(AuthRequest request) + { + if (AuthRequested != null) + { + AuthRequested(this, request); + return true; + } + + return false; + } + + bool IAuthClient.OnAuthResponse(AuthErrorResponse errorResponse) + { + if (AuthError != null) + { + AuthError(this, errorResponse); + return true; + } + + return false; + } + + bool IAuthClient.OnAuthResponse(AuthResponse response) + { + if (AuthResponded != null) + { + AuthResponded(this, response); + return true; + } + + return false; + } +} diff --git a/WalletConnectSharp.Auth/Cacao.cs b/WalletConnectSharp.Auth/Cacao.cs new file mode 100644 index 0000000..b516bdc --- /dev/null +++ b/WalletConnectSharp.Auth/Cacao.cs @@ -0,0 +1,126 @@ +using Newtonsoft.Json; +using WalletConnectSharp.Common.Utils; +using WalletConnectSharp.Network.Models; + +namespace WalletConnectSharp.Auth.Models; + +[RpcResponseOptions(Clock.ONE_MINUTE, 3001)] +public class Cacao : Message +{ + public struct CacaoHeader + { + [JsonProperty] + public readonly string t = "eip4361"; + + public CacaoHeader() + { + } + } + + public class CacaoRequestPayload + { + [JsonProperty("domain")] + public string Domain { get; set; } + + [JsonProperty("aud")] + public string Aud { get; set; } + + [JsonProperty("version")] + public string Version { get; set; } + + [JsonProperty("nonce")] + public string Nonce { get; set; } + + [JsonProperty("iat")] + public string Iat { get; set; } + + [JsonProperty("nbf")] + public string Nbf { get; set; } + + [JsonProperty("exp")] + public string Exp { get; set; } + + [JsonProperty("chainId")] + public string ChainId { get; set; } + + [JsonProperty("statement")] + public string Statement { get; set; } + + [JsonProperty("requestId")] + public string RequestId { get; set; } + + [JsonProperty("resources")] + public string[] Resource { get; set; } + } + + public class CacaoPayload : CacaoRequestPayload + { + [JsonProperty("iss")] + public string Iss { get; set; } + + public CacaoPayload(CacaoRequestPayload source) + { + this.Aud = source.Aud; + this.Domain = source.Domain; + this.Exp = source.Exp; + this.Iat = source.Iat; + this.Nbf = source.Nbf; + this.Nonce = source.Nonce; + this.Resource = source.Resource; + this.Statement = source.Statement; + this.Version = source.Version; + this.ChainId = source.ChainId; + this.RequestId = source.RequestId; + } + + public CacaoPayload(CacaoRequestPayload source, string iss) + { + this.Aud = source.Aud; + this.Domain = source.Domain; + this.Exp = source.Exp; + this.Iat = source.Iat; + this.Nbf = source.Nbf; + this.Nonce = source.Nonce; + this.Resource = source.Resource; + this.Statement = source.Statement; + this.Version = source.Version; + this.ChainId = source.ChainId; + this.RequestId = source.RequestId; + + this.Iss = iss; + } + } + + public abstract class CacaoSignature + { + [JsonProperty("t")] + public readonly string T; + + public class EIP191CacaoSignature : CacaoSignature + { + [JsonProperty("t")] + public readonly string T = "eip191"; + } + + public class EIP1271CacaoSignature : CacaoSignature + { + [JsonProperty("t")] + public readonly string T = "eip1271"; + } + + [JsonProperty("s")] + public string S { get; set; } + + [JsonProperty("m")] + public string M { get; set; } + } + + [JsonProperty("h")] + public readonly CacaoHeader Header = new CacaoHeader(); + + [JsonProperty("p")] + public CacaoPayload Payload { get; set; } + + [JsonProperty("s")] + public CacaoSignature Signature { get; set; } +} diff --git a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs new file mode 100644 index 0000000..a755b69 --- /dev/null +++ b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs @@ -0,0 +1,372 @@ +using Newtonsoft.Json; +using WalletConnectSharp.Auth.Interfaces; +using WalletConnectSharp.Auth.Internals; +using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Auth.Models.Engine; +using WalletConnectSharp.Common.Model.Errors; +using WalletConnectSharp.Common.Utils; +using WalletConnectSharp.Core.Models.Relay; +using WalletConnectSharp.Core.Models.Verify; +using WalletConnectSharp.Crypto.Models; +using WalletConnectSharp.Events; +using WalletConnectSharp.Events.Model; +using WalletConnectSharp.Network.Models; +using CommonErrorResponse = WalletConnectSharp.Network.Models.ErrorResponse; +using ErrorResponse = WalletConnectSharp.Auth.Models.ErrorResponse; + +namespace WalletConnectSharp.Auth.Controllers; + +public partial class AuthEngine : IAuthEngine +{ + private bool initialized = false; + + + public const string AUTH_CLIENT_PROTOCOL = "wc"; + public const int AUTH_CLIENT_VERSION = 1; + public const string AUTH_CLIENT_CONTEXT = "auth"; + + public static readonly string AUTH_CLIENT_STORAGE_PREFIX = + $"{AUTH_CLIENT_PROTOCOL}@{AUTH_CLIENT_VERSION}:{AUTH_CLIENT_CONTEXT}"; + + public static readonly string AUTH_CLIENT_PUBLIC_KEY_NAME = $"{AUTH_CLIENT_STORAGE_PREFIX}:PUB_KEY"; + + public string Name + { + get + { + return "authEngine"; + } + } + + public string Context + { + get + { + return $"{Name}-context"; + } + } + + public IAuthClient Client { get; } + + public IDictionary PendingRequests + { + get + { + return this.Client.Requests.Values.OfType().Where(obj => obj.Id != null).GroupBy(obj => (long)obj.Id).ToDictionary(x => x.Key, x => x.First()); + } + } + + public AuthEngine(IAuthClient client) + { + Client = client; + } + + public void Init() + { + if (!initialized) + { + RegisterRelayerEvents(); + this.initialized = true; + } + } + + public async Task Request(RequestParams @params, string topic = null) + { + IsInitialized(); + + if (!IsValidRequest(@params)) + { + throw new ArgumentException("Invalid request", nameof(@params)); + } + + if (topic != null) + { + return await this.RequestOnKnownPairing(topic, @params); + } + + var data = await this.Client.Core.Pairing.Create(); + var pairingTopic = data.Topic; + var uri = data.Uri; + + // TODO Log + + var publicKey = await this.Client.Core.Crypto.GenerateKeyPair(); + var responseTopic = this.Client.Core.Crypto.HashKey(publicKey); + + await this.Client.AuthKeys.Set(AUTH_CLIENT_PUBLIC_KEY_NAME, + new AuthData() { PublicKey = publicKey, ResponseTopic = responseTopic }); + await this.Client.PairingTopics.Set(responseTopic, + new PairingData() { Topic = responseTopic, PairingTopic = pairingTopic }); + + await this.Client.Core.Relayer.Subscribe(responseTopic); + + // TODO Log + + var id = await this.SendRequest(pairingTopic, new WcAuthRequest() + { + Payload = new PayloadParams() + { + Type = @params.Type ?? new Cacao.CacaoHeader(), + ChainId = @params.ChainId, + Statement = @params.Statement, + Aud = @params.Aud, + Domain = @params.Domain, + Version = "1", + Nonce = @params.Nonce, + Iat = DateTime.Now.ToISOString(), + }, + Requester = new Requester() { Metadata = this.Client.Metadata, PublicKey = publicKey } + }, @params.Expiry); + + // TODO Log + + return new RequestUri() { Uri = uri, Id = id }; + } + + private async Task RequestOnKnownPairing(string topic, RequestParams @params) + { + var knownPairing = this.Client.Core.Pairing.Pairings.First(p => p.Active.HasValue && p.Active.Value && p.Topic == topic); + + var publicKey = this.Client.AuthKeys.Get(AUTH_CLIENT_PUBLIC_KEY_NAME); + + var id = await this.SendRequest(knownPairing.Topic, + new WcAuthRequest() + { + Payload = new PayloadParams() + { + Type = @params.Type ?? new Cacao.CacaoHeader(), + ChainId = @params.ChainId, + Statement = @params.Statement, + Aud = @params.Aud, + Domain = @params.Domain, + Version = "1", + Nonce = @params.Nonce, + Iat = DateTime.Now.ToISOString() + }, + Requester = new Requester() { PublicKey = publicKey.PublicKey, Metadata = this.Client.Metadata } + }, @params.Expiry); + + // TODO Log + + return new RequestUri() { Id = id }; + } + + public async Task Respond(Message message, string iss) + { + this.IsInitialized(); + + if (message.Id == null || !IsValidRespond(message, this.Client.Requests)) + { + throw new Exception("Invalid response"); + } + + var pendingRequest = GetPendingRequest(this.Client.Requests, (long)message.Id); + + if (pendingRequest == null || pendingRequest.Id == null) + { + throw new Exception("Invalid pending request stored"); + } + + var id = (long)pendingRequest.Id; + var receiverPublicKey = pendingRequest.Requester.PublicKey; + var senderPublicKey = await this.Client.Core.Crypto.GenerateKeyPair(); + var responseTopic = this.Client.Core.Crypto.HashKey(receiverPublicKey); + var encodeOptions = new EncodeOptions() + { + Type = Crypto.Crypto.TYPE_1, ReceiverPublicKey = receiverPublicKey, SenderPublicKey = senderPublicKey + }; + + if (message is ErrorResponse errorResponse) + { + await this.SendError(id, responseTopic, errorResponse, encodeOptions); + return; + } + + var cacao = new Cacao() + { + Payload = new Cacao.CacaoPayload(pendingRequest.CacaoPayload) { Iss = iss }, + Signature = ((ResultResponse)message).Signature + }; + + await this.SendResult(id, responseTopic, cacao, encodeOptions); + + await this.Client.Requests.Update(id, cacao); + } + + protected Task SendRequest(string topic, WcAuthRequest request, long? expiry = null, EncodeOptions options = null) + { + return this.Client.Core.MessageHandler.SendRequest(topic, request, expiry, options); + } + + protected Task SendError(long id, string topic, ErrorResponse response, EncodeOptions options = null) + { + return this.Client.Core.MessageHandler.SendError(id, topic, response.Error, options); + } + + protected Task SendResult(long id, string topic, Cacao result, EncodeOptions options = null) + { + return this.Client.Core.MessageHandler.SendResult(id, topic, result, options); + } + + protected async Task SetExpiry(string topic, long expiry) + { + if (this.Client.Core.Pairing.Store.Keys.Contains(topic)) + { + await this.Client.Core.Pairing.UpdateExpiry(topic, expiry); + } + this.Client.Core.Expirer.Set(topic, expiry); + } + + private void RegisterRelayerEvents() + { + // MessageHandler will handle all topic tracking + this.Client.Core.MessageHandler.HandleMessageType(OnAuthRequest, OnAuthResponse); + } + + private async Task OnAuthResponse(string topic, JsonRpcResponse response) + { + var id = response.Id; + + if (!response.IsError) + { + var pairingTopic = this.Client.PairingTopics.Get(topic); + var signature = response.Result.Signature; + var payload = response.Result.Payload; + + await this.Client.Requests.Set(id, response.Result); + var reconstructed = FormatMessage(payload, payload.Iss); + + // TODO Log + + var walletAddress = IssDidUtils.DidAddress(payload.Iss); + var chainId = IssDidUtils.DidChainId(payload.Iss); + + if (string.IsNullOrWhiteSpace(walletAddress)) + { + throw new ArgumentException("Could not derive address from iss in payload"); + } + + if (string.IsNullOrWhiteSpace(chainId)) + { + throw new ArgumentException("Could not derive chainId from iss in payload"); + } + + var isValid = await SignatureUtils.VerifySignature(walletAddress, reconstructed, signature, chainId, + this.Client.ProjectId); + + if (!isValid) + { + this.Client.OnAuthResponse(new AuthErrorResponse() + { + Id = id, Topic = topic, Error = CommonErrorResponse.FromErrorType(ErrorType.GENERIC, new + { + Message = "Invalid signature" + }) + }); + } + else + { + this.Client.OnAuthResponse(new AuthResponse() { Id = id, Topic = topic, Response = response }); + } + } + else + { + this.Client.OnAuthResponse(new AuthErrorResponse() { Id = id, Topic = topic, Error = response.Error }); + } + } + + private async Task OnAuthRequest(string topic, JsonRpcRequest payload) + { + var payloadParams = payload.Params.Payload; + var cacaoPayload = new Cacao.CacaoRequestPayload() + { + Aud = payloadParams.Aud, + ChainId = payloadParams.ChainId, + Domain = payloadParams.Domain, + Exp = payloadParams.Exp, + Iat = payloadParams.Iat, + Nbf = payloadParams.Nbf, + Nonce = payloadParams.Nonce, + RequestId = payloadParams.RequestId, + Resource = payloadParams.Resources, + Statement = payloadParams.Statement + }; + + await this.Client.Requests.Set(payload.Id, + new PendingRequest() + { + CacaoPayload = cacaoPayload, + Id = payload.Id, + PairingTopic = topic, + Requester = payload.Params.Requester + }); + + var hash = HashUtils.HashMessage(JsonConvert.SerializeObject(payload)); + var verifyContext = await GetVerifyContext(hash, this.Client.Metadata); + + this.Client.OnAuthRequest(new AuthRequest() + { + Id = payload.Id, + Topic = topic, + Parameters = + new AuthRequestData() { CacaoPayload = cacaoPayload, Requester = payload.Params.Requester }, + VerifyContext = verifyContext + }); + } + + private async Task GetVerifyContext(string hash, Metadata metadata) + { + var context = new VerifiedContext() + { + VerifyUrl = metadata.VerifyUrl, Validation = Validation.Unknown, Origin = metadata.Url + }; + try + { + + var origin = await this.Client.Core.Verify.Resolve(hash); + + if (string.IsNullOrWhiteSpace(origin)) + { + return context; + } + + context.Origin = origin; + context.Validation = origin == metadata.Url ? Validation.Valid : Validation.Invalid; + } + catch (Exception e) + { + // TODO Log exception + } + + return context; + } + + public string FormatMessage(Cacao.CacaoPayload cacao, string iss) + { + var header = $"{cacao.Domain} wants you to sign in with your Ethereum account:"; + var walletAddress = IssDidUtils.DidAddress(iss); + var statement = cacao.Statement; + var uri = $"URI: {cacao.Aud}"; + var version = $"Version: {cacao.Version}"; + var chainId = $"Chain ID: {IssDidUtils.DidChainId(iss)}"; + var nonce = $"Nonce: {cacao.Nonce}"; + var issuedAt = $"Issued At: {cacao.Iat}"; + var resources = cacao.Resource != null && cacao.Resource.Length > 0 + ? $"Resources:\n{string.Join('\n', cacao.Resource.Select((resource) => $"- {resource}"))}" + : null; + + var message = string.Join('\n', + new string[] { header, walletAddress, "", statement, "", uri, version, chainId, nonce, issuedAt, resources } + .Where(val => !string.IsNullOrWhiteSpace(val))); + + return message; + } + + private void IsInitialized() + { + if (!this.initialized) + { + throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, Name); + } + } +} diff --git a/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs b/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs new file mode 100644 index 0000000..f5a3047 --- /dev/null +++ b/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs @@ -0,0 +1,41 @@ +using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Common; +using WalletConnectSharp.Core.Interfaces; +using WalletConnectSharp.Events.Interfaces; + +namespace WalletConnectSharp.Auth.Interfaces; + +public interface IAuthClient : IModule, IEvents +{ + string Protocol { get; } + int Version { get; } + + event EventHandler AuthRequested; + event EventHandler AuthResponded; + event EventHandler AuthError; + + ICore Core { get; set; } + Metadata Metadata { get; set; } + string ProjectId { get; set; } + IStore AuthKeys { get; set; } + IStore PairingTopics { get; set; } + IStore Requests { get; set; } + + IAuthEngine Engine { get; } + + AuthOptions Options { get; } + + IDictionary PendingRequests { get; } + + Task Request(RequestParams @params, string topic = null); + + Task Respond(Message message, string iss); + + string FormatMessage(Cacao.CacaoPayload cacao, string iss); + + internal bool OnAuthRequest(AuthRequest request); + + internal bool OnAuthResponse(AuthErrorResponse errorResponse); + + internal bool OnAuthResponse(AuthResponse response); +} diff --git a/WalletConnectSharp.Auth/Interfaces/IAuthEngine.cs b/WalletConnectSharp.Auth/Interfaces/IAuthEngine.cs new file mode 100644 index 0000000..190c77c --- /dev/null +++ b/WalletConnectSharp.Auth/Interfaces/IAuthEngine.cs @@ -0,0 +1,19 @@ +using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Common; + +namespace WalletConnectSharp.Auth.Interfaces; + +public interface IAuthEngine : IModule +{ + IAuthClient Client { get; } + + IDictionary PendingRequests { get; } + + void Init(); + + Task Request(RequestParams @params, string topic = null); + + Task Respond(Message message, string iss); + + string FormatMessage(Cacao.CacaoPayload cacao, string iss); +} diff --git a/WalletConnectSharp.Auth/Internals/AuthEngineValidations.cs b/WalletConnectSharp.Auth/Internals/AuthEngineValidations.cs new file mode 100644 index 0000000..bd3b476 --- /dev/null +++ b/WalletConnectSharp.Auth/Internals/AuthEngineValidations.cs @@ -0,0 +1,47 @@ +using WalletConnectSharp.Auth.Interfaces; +using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Common.Model.Errors; +using WalletConnectSharp.Common.Utils; +using WalletConnectSharp.Core; +using WalletConnectSharp.Core.Interfaces; + +namespace WalletConnectSharp.Auth.Controllers; + +public partial class AuthEngine : IAuthEngine +{ + public const long MinExpiry = Clock.FIVE_MINUTES; + public const long MaxExpiry = Clock.SEVEN_DAYS; + + internal bool IsValidRequest(RequestParams @params) + { + var validAudience = Utils.IsValidUrl(@params.Aud); + // TODO: From typescript + // FIXME: disabling this temporarily since it's failing expected values like `chainId: "1"` + // const validChainId = isValidChainId(params.chainId); + var domainInAud = @params.Aud.Contains(@params.Domain); + var hasNonce = !string.IsNullOrWhiteSpace(@params.Nonce); + var hasValidType = @params.Type is { t: "eip4361" }; + 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}"); + } + + return validAudience && domainInAud && hasNonce && hasValidType; + } + + internal PendingRequest GetPendingRequest(IStore pendingResponses, long id) + { + return pendingResponses.Values.OfType().FirstOrDefault(request => request.Id == id); + } + + internal bool IsValidRespond(Message @params, IStore pendingResponses) + { + if (@params.Id == null) + return false; + + var validId = GetPendingRequest(pendingResponses, (long)@params.Id); + + return validId != null; + } +} diff --git a/WalletConnectSharp.Auth/Internals/IssDidUtils.cs b/WalletConnectSharp.Auth/Internals/IssDidUtils.cs index f76e824..1614574 100644 --- a/WalletConnectSharp.Auth/Internals/IssDidUtils.cs +++ b/WalletConnectSharp.Auth/Internals/IssDidUtils.cs @@ -1,4 +1,6 @@ -namespace WalletConnectSharp.Auth.Internals +using System.Globalization; + +namespace WalletConnectSharp.Auth.Internals { public static class IssDidUtils { @@ -40,5 +42,10 @@ public static string DidAddress(string iss) return segments[segments.Length - 1]; } + + public static string ToISOString(this DateTime dateTime) + { + return dateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fffK", CultureInfo.InvariantCulture); + } } } diff --git a/WalletConnectSharp.Auth/Internals/SignatureUtils.cs b/WalletConnectSharp.Auth/Internals/SignatureUtils.cs new file mode 100644 index 0000000..1b13ac8 --- /dev/null +++ b/WalletConnectSharp.Auth/Internals/SignatureUtils.cs @@ -0,0 +1,76 @@ +using Newtonsoft.Json; +using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Common.Utils; +using WalletConnectSharp.Core.Models.Eth; +using WalletConnectSharp.Network.Models; + +namespace WalletConnectSharp.Auth.Internals; + +public class SignatureUtils +{ + public const string DefaultRpcUrl = "https://rpc.walletconnect.com/v1"; + + public static async Task VerifySignature(string address, string reconstructedMessage, Cacao.CacaoSignature cacaoSignature, + string chainId, string projectId) + { + switch (cacaoSignature.T) + { + case "eip191": + return IsValidEip191Signature(address, reconstructedMessage, cacaoSignature.S); + case "eip1271": + return await IsValidEip1271Signature(address, reconstructedMessage, cacaoSignature.S, chainId, projectId); + default: + throw new ArgumentException( + $"VerifySignature Failed: Attempted to verify CacaoSignature with unknown type {cacaoSignature.T}"); + } + } + + private static async Task IsValidEip1271Signature(string address, string reconstructedMessage, string cacaoSignatureS, string chainId, string projectId) + { + var eip1271MagicValue = "0x1626ba7e"; + var dynamicTypeOffset = "0000000000000000000000000000000000000000000000000000000000000040"; + var dynamicTypeLength = "0000000000000000000000000000000000000000000000000000000000000041"; + var nonPrefixedSignature = cacaoSignatureS.Substring(2); + var nonPrefixedHashedMessage = HashUtils.HashMessage(reconstructedMessage).Substring(2); + + var data = + eip1271MagicValue + + nonPrefixedHashedMessage + + dynamicTypeOffset + + dynamicTypeLength + + nonPrefixedSignature; + + string result = null; + using (var client = new HttpClient()) + { + var url = $"{DefaultRpcUrl}/?chainId={chainId}&projectId={projectId}"; + + var rpcRequest = new JsonRpcRequest("eth_call", + new object[] { new EthCall() { To = address, Data = data }, "latest" }); + + var httpResponse = await client.PostAsync(url, new StringContent(JsonConvert.SerializeObject(rpcRequest))); + + var jsonResponse = await httpResponse.Content.ReadAsStringAsync(); + + var response = JsonConvert.DeserializeObject>(jsonResponse); + + if (response != null) + result = response.Result; + else + throw new Exception($"Could not deserialize JsonRpcResponse from JSON {jsonResponse}"); + } + + if (string.IsNullOrWhiteSpace(result) || result == "0x") + { + return false; + } + + var recoveredValue = result.Substring(0, eip1271MagicValue.Length); + return recoveredValue.ToLower() == eip1271MagicValue.ToLower(); + } + + private static bool IsValidEip191Signature(string address, string reconstructedMessage, string cacaoSignatureS) + { + return false; + } +} diff --git a/WalletConnectSharp.Auth/Models/AuthData.cs b/WalletConnectSharp.Auth/Models/AuthData.cs new file mode 100644 index 0000000..a0ad230 --- /dev/null +++ b/WalletConnectSharp.Auth/Models/AuthData.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; +using WalletConnectSharp.Core.Interfaces; + +namespace WalletConnectSharp.Auth.Models; + +public class AuthData : IKeyHolder +{ + [JsonProperty("responseTopic")] + public string ResponseTopic { get; set; } + + [JsonProperty("publicKey")] + public string PublicKey { get; set; } + + public string Key + { + get + { + return ResponseTopic; + } + } +} diff --git a/WalletConnectSharp.Auth/Models/AuthErrorResponse.cs b/WalletConnectSharp.Auth/Models/AuthErrorResponse.cs new file mode 100644 index 0000000..68358f1 --- /dev/null +++ b/WalletConnectSharp.Auth/Models/AuthErrorResponse.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; +using CommonErrorResponse = WalletConnectSharp.Network.Models.ErrorResponse; + +namespace WalletConnectSharp.Auth.Models; + +public class AuthErrorResponse : TopicMessage +{ + [JsonProperty("params")] + public CommonErrorResponse Error { get; set; } +} diff --git a/WalletConnectSharp.Auth/Models/AuthOptions.cs b/WalletConnectSharp.Auth/Models/AuthOptions.cs new file mode 100644 index 0000000..a1e52f2 --- /dev/null +++ b/WalletConnectSharp.Auth/Models/AuthOptions.cs @@ -0,0 +1,11 @@ +using WalletConnectSharp.Core.Interfaces; +using WalletConnectSharp.Core.Models; + +namespace WalletConnectSharp.Auth.Models; + +public class AuthOptions : CoreOptions +{ + public Metadata Metadata { get; set; } + + public ICore Core { get; set; } +} diff --git a/WalletConnectSharp.Auth/Models/AuthPayload.cs b/WalletConnectSharp.Auth/Models/AuthPayload.cs new file mode 100644 index 0000000..1e23843 --- /dev/null +++ b/WalletConnectSharp.Auth/Models/AuthPayload.cs @@ -0,0 +1,37 @@ +using Newtonsoft.Json; + +namespace WalletConnectSharp.Auth.Models; + +public class AuthPayload +{ + [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)] + public Cacao.CacaoHeader? Type { get; set; } + + [JsonProperty("chainId")] + public string ChainId { get; set; } + + [JsonProperty("domain")] + public string Domain { get; set; } + + [JsonProperty("aud")] + public string Aud { get; set; } + + [JsonProperty("nonce")] + public string Nonce { get; set; } + + [JsonProperty("nbf", NullValueHandling = NullValueHandling.Ignore)] + public string Nbf { get; set; } + + [JsonProperty("exp", NullValueHandling = NullValueHandling.Ignore)] + public string Exp { get; set; } + + [JsonProperty("statement", NullValueHandling = NullValueHandling.Ignore)] + public string Statement { get; set; } + + [JsonProperty("requestId", NullValueHandling = NullValueHandling.Ignore)] + public string RequestId { get; set; } + + [JsonProperty("resources", NullValueHandling = NullValueHandling.Ignore)] + public string[] Resources { get; set; } + +} diff --git a/WalletConnectSharp.Auth/Models/AuthRequest.cs b/WalletConnectSharp.Auth/Models/AuthRequest.cs new file mode 100644 index 0000000..d0948d0 --- /dev/null +++ b/WalletConnectSharp.Auth/Models/AuthRequest.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; +using WalletConnectSharp.Core.Models.Verify; + +namespace WalletConnectSharp.Auth.Models; + +public class AuthRequest : TopicMessage +{ + [JsonProperty("params")] + public AuthRequestData Parameters { get; set; } + + [JsonProperty("verifyContext")] + public VerifiedContext VerifyContext { get; set; } +} diff --git a/WalletConnectSharp.Auth/Models/AuthRequestData.cs b/WalletConnectSharp.Auth/Models/AuthRequestData.cs new file mode 100644 index 0000000..505784d --- /dev/null +++ b/WalletConnectSharp.Auth/Models/AuthRequestData.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; +using WalletConnectSharp.Core.Models.Verify; + +namespace WalletConnectSharp.Auth.Models; + +public class AuthRequestData +{ + [JsonProperty("cacaoPayload")] + public Cacao.CacaoRequestPayload CacaoPayload { get; set; } + + [JsonProperty("requester")] + public Requester Requester { get; set; } +} diff --git a/WalletConnectSharp.Auth/Models/AuthResponse.cs b/WalletConnectSharp.Auth/Models/AuthResponse.cs new file mode 100644 index 0000000..a943d5a --- /dev/null +++ b/WalletConnectSharp.Auth/Models/AuthResponse.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; +using WalletConnectSharp.Network.Models; + +namespace WalletConnectSharp.Auth.Models; + +public class AuthResponse : TopicMessage +{ + [JsonProperty("params")] + public JsonRpcResponse Response { get; set; } +} diff --git a/WalletConnectSharp.Auth/Models/ErrorResponse.cs b/WalletConnectSharp.Auth/Models/ErrorResponse.cs new file mode 100644 index 0000000..08aa465 --- /dev/null +++ b/WalletConnectSharp.Auth/Models/ErrorResponse.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; +using CommonErrorResponse = WalletConnectSharp.Network.Models.ErrorResponse; +namespace WalletConnectSharp.Auth.Models; + +public class ErrorResponse : Message +{ + [JsonProperty("error")] + public CommonErrorResponse Error { get; set; } +} diff --git a/WalletConnectSharp.Auth/Models/Message.cs b/WalletConnectSharp.Auth/Models/Message.cs new file mode 100644 index 0000000..4b5ca80 --- /dev/null +++ b/WalletConnectSharp.Auth/Models/Message.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; +using WalletConnectSharp.Core.Interfaces; + +namespace WalletConnectSharp.Auth.Models; + +public class Message : IKeyHolder +{ + [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)] + public long? Id { get; set; } + + public long Key + { + get + { + if (Id != null) + return (long)Id; + throw new KeyNotFoundException("Id Key for message instance is null: " + this.ToString()); + } + } +} diff --git a/WalletConnectSharp.Auth/Models/Metadata.cs b/WalletConnectSharp.Auth/Models/Metadata.cs new file mode 100644 index 0000000..bae7f84 --- /dev/null +++ b/WalletConnectSharp.Auth/Models/Metadata.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; + +namespace WalletConnectSharp.Auth.Models; + +public class Metadata +{ + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("url")] + public string Url { get; set; } + + [JsonProperty("icons")] + public string[] Icons { get; set; } + + [JsonProperty("redirect")] + public RedirectData Redirect { get; set; } + + [JsonProperty("verifyUrl")] + public string VerifyUrl { get; set; } +} diff --git a/WalletConnectSharp.Auth/Models/PairingData.cs b/WalletConnectSharp.Auth/Models/PairingData.cs new file mode 100644 index 0000000..baf8c1e --- /dev/null +++ b/WalletConnectSharp.Auth/Models/PairingData.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; +using WalletConnectSharp.Core.Interfaces; + +namespace WalletConnectSharp.Auth.Models; + +public class PairingData : IKeyHolder +{ + [JsonProperty("topic")] + public string Topic { get; set; } + + [JsonProperty("pairingTopic")] + public string PairingTopic { get; set; } + + public string Key + { + get + { + return PairingTopic; + } + } +} diff --git a/WalletConnectSharp.Auth/Models/PayloadParams.cs b/WalletConnectSharp.Auth/Models/PayloadParams.cs new file mode 100644 index 0000000..6120446 --- /dev/null +++ b/WalletConnectSharp.Auth/Models/PayloadParams.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace WalletConnectSharp.Auth.Models; + +public class PayloadParams : AuthPayload +{ + [JsonProperty("version")] + public string Version { get; set; } + + [JsonProperty("iat")] + public string Iat { get; set; } +} diff --git a/WalletConnectSharp.Auth/Models/PendingRequest.cs b/WalletConnectSharp.Auth/Models/PendingRequest.cs new file mode 100644 index 0000000..3499eaa --- /dev/null +++ b/WalletConnectSharp.Auth/Models/PendingRequest.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace WalletConnectSharp.Auth.Models; + +public class PendingRequest : Message +{ + [JsonProperty("pairingTopic")] + public string PairingTopic { get; set; } + + [JsonProperty("requester")] + public Requester Requester { get; set; } + + [JsonProperty("cacaoPayload")] + public Cacao.CacaoRequestPayload CacaoPayload { get; set; } +} diff --git a/WalletConnectSharp.Auth/Models/RedirectData.cs b/WalletConnectSharp.Auth/Models/RedirectData.cs new file mode 100644 index 0000000..ae3ee1e --- /dev/null +++ b/WalletConnectSharp.Auth/Models/RedirectData.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace WalletConnectSharp.Auth.Models; + +public class RedirectData +{ + [JsonProperty("native")] + public string Native { get; set; } + + [JsonProperty("universal")] + public string Universal { get; set; } +} diff --git a/WalletConnectSharp.Auth/Models/RequestParams.cs b/WalletConnectSharp.Auth/Models/RequestParams.cs new file mode 100644 index 0000000..2025908 --- /dev/null +++ b/WalletConnectSharp.Auth/Models/RequestParams.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace WalletConnectSharp.Auth.Models; + +public class RequestParams : AuthPayload +{ + [JsonProperty("expiry", NullValueHandling = NullValueHandling.Ignore)] + public long? Expiry { get; set; } +} diff --git a/WalletConnectSharp.Auth/Models/RequestUri.cs b/WalletConnectSharp.Auth/Models/RequestUri.cs new file mode 100644 index 0000000..f3f42d3 --- /dev/null +++ b/WalletConnectSharp.Auth/Models/RequestUri.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace WalletConnectSharp.Auth.Models; + +public class RequestUri +{ + [JsonProperty("id")] + public long Id { get; set; } + + [JsonProperty("uri")] + public string Uri { get; set; } +} diff --git a/WalletConnectSharp.Auth/Models/Requester.cs b/WalletConnectSharp.Auth/Models/Requester.cs new file mode 100644 index 0000000..a1c6473 --- /dev/null +++ b/WalletConnectSharp.Auth/Models/Requester.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace WalletConnectSharp.Auth.Models; + +public class Requester +{ + [JsonProperty("metadata")] + public Metadata Metadata { get; set; } + + [JsonProperty("publicKey")] + public string PublicKey { get; set; } +} diff --git a/WalletConnectSharp.Auth/Models/ResultResponse.cs b/WalletConnectSharp.Auth/Models/ResultResponse.cs new file mode 100644 index 0000000..2e60457 --- /dev/null +++ b/WalletConnectSharp.Auth/Models/ResultResponse.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace WalletConnectSharp.Auth.Models; + +public class ResultResponse : Message +{ + [JsonProperty("signature")] + public Cacao.CacaoSignature Signature { get; set; } +} diff --git a/WalletConnectSharp.Auth/Models/TopicMessage.cs b/WalletConnectSharp.Auth/Models/TopicMessage.cs new file mode 100644 index 0000000..6026473 --- /dev/null +++ b/WalletConnectSharp.Auth/Models/TopicMessage.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace WalletConnectSharp.Auth.Models; + +public class TopicMessage : Message +{ + [JsonProperty("topic")] + public string Topic { get; set; } +} diff --git a/WalletConnectSharp.Auth/WalletConnectSharp.Auth.csproj b/WalletConnectSharp.Auth/WalletConnectSharp.Auth.csproj index 6d7bad0..cec29da 100644 --- a/WalletConnectSharp.Auth/WalletConnectSharp.Auth.csproj +++ b/WalletConnectSharp.Auth/WalletConnectSharp.Auth.csproj @@ -27,10 +27,4 @@ - - - - - - diff --git a/WalletConnectSharp.Auth/WcAuthRequest.cs b/WalletConnectSharp.Auth/WcAuthRequest.cs new file mode 100644 index 0000000..81fe26b --- /dev/null +++ b/WalletConnectSharp.Auth/WcAuthRequest.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; +using WalletConnectSharp.Common.Utils; +using WalletConnectSharp.Network.Models; + +namespace WalletConnectSharp.Auth.Models.Engine; + +[RpcMethod("wc_authRequest")] +[RpcRequestOptions(Clock.ONE_DAY, 3000)] +[RpcResponseOptions(Clock.ONE_DAY, 3001)] +public class WcAuthRequest +{ + [JsonProperty("payloadParams")] + public PayloadParams Payload { get; set; } + + [JsonProperty("requester")] + public Requester Requester { get; set; } +} diff --git a/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs b/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs index 5189304..a37bf00 100644 --- a/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs +++ b/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs @@ -1,6 +1,7 @@ using WalletConnectSharp.Common.Model.Errors; using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Core.Models.Relay; +using WalletConnectSharp.Crypto.Models; using WalletConnectSharp.Events; using WalletConnectSharp.Events.Model; using WalletConnectSharp.Network.Models; @@ -265,13 +266,13 @@ public PublishOptions RpcResponseOptionsForType() /// The request type /// The response type /// The id of the request sent - public async Task SendRequest(string topic, T parameters, long? expiry = null) + public async Task SendRequest(string topic, T parameters, long? expiry = null, EncodeOptions options = null) { var method = RpcMethodAttribute.MethodForType(); var payload = new JsonRpcRequest(method, parameters); - var message = await this.Core.Crypto.Encode(topic, payload); + var message = await this.Core.Crypto.Encode(topic, payload, options); var opts = RpcRequestOptionsFromType(); @@ -299,10 +300,10 @@ public async Task SendRequest(string topic, T parameters, long? exp /// The typed response message to send /// The request type /// The response type - public async Task SendResult(long id, string topic, TR result) + public async Task SendResult(long id, string topic, TR result, EncodeOptions options = null) { var payload = new JsonRpcResponse(id, null, result); - var message = await this.Core.Crypto.Encode(topic, payload); + var message = await this.Core.Crypto.Encode(topic, payload, options); var opts = RpcResponseOptionsFromTypes(); await this.Core.Relayer.Publish(topic, message, opts); await (await this.Core.History.JsonRpcHistoryOfType()).Resolve(payload); @@ -316,10 +317,10 @@ public async Task SendResult(long id, string topic, TR result) /// The error response to send /// The request type /// The response type - public async Task SendError(long id, string topic, ErrorResponse error) + public async Task SendError(long id, string topic, ErrorResponse error, EncodeOptions options = null) { var payload = new JsonRpcResponse(id, error, default); - var message = await this.Core.Crypto.Encode(topic, payload); + var message = await this.Core.Crypto.Encode(topic, payload, options); var opts = RpcResponseOptionsFromTypes(); await this.Core.Relayer.Publish(topic, message, opts); await (await this.Core.History.JsonRpcHistoryOfType()).Resolve(payload); diff --git a/WalletConnectSharp.Core/Core.cs b/WalletConnectSharp.Core/Core.cs index 7c20077..35f2d70 100644 --- a/WalletConnectSharp.Core/Core.cs +++ b/WalletConnectSharp.Core/Core.cs @@ -4,6 +4,7 @@ using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Core.Models; using WalletConnectSharp.Core.Models.Relay; +using WalletConnectSharp.Core.Models.Verify; using WalletConnectSharp.Crypto; using WalletConnectSharp.Crypto.Interfaces; using WalletConnectSharp.Events; @@ -110,6 +111,12 @@ public string Context /// with each other and keeping track of pairing state /// public IPairing Pairing { get; } + + /// + /// The module that is used to resolve origins + /// of a given attestation Id. + /// + public Verifier Verify { get; } /// /// Create a new Core with the given options. @@ -148,6 +155,7 @@ public Core(CoreOptions options = null) Events = new EventDelegator(this); Expirer = new Expirer(this); Pairing = new Pairing(this); + Verify = new Verifier(); Relayer = new Relayer(new RelayerOptions() { diff --git a/WalletConnectSharp.Core/Interfaces/ICore.cs b/WalletConnectSharp.Core/Interfaces/ICore.cs index 245c761..0471594 100644 --- a/WalletConnectSharp.Core/Interfaces/ICore.cs +++ b/WalletConnectSharp.Core/Interfaces/ICore.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using WalletConnectSharp.Common; +using WalletConnectSharp.Core.Models.Verify; using WalletConnectSharp.Crypto.Interfaces; using WalletConnectSharp.Events.Interfaces; using WalletConnectSharp.Storage.Interfaces; @@ -77,6 +78,8 @@ public interface ICore : IModule, IEvents /// with each other and keeping track of pairing state /// IPairing Pairing { get; } + + Verifier Verify { get; } /// /// Start the Core module, which will initialize all modules the Core module uses diff --git a/WalletConnectSharp.Core/Interfaces/ITypedMessageHandler.cs b/WalletConnectSharp.Core/Interfaces/ITypedMessageHandler.cs index e03c9e7..9301ef6 100644 --- a/WalletConnectSharp.Core/Interfaces/ITypedMessageHandler.cs +++ b/WalletConnectSharp.Core/Interfaces/ITypedMessageHandler.cs @@ -1,5 +1,6 @@ using WalletConnectSharp.Common; using WalletConnectSharp.Core.Models.Relay; +using WalletConnectSharp.Crypto.Models; using WalletConnectSharp.Events.Interfaces; using WalletConnectSharp.Network.Models; @@ -80,10 +81,11 @@ void HandleMessageType(Func, Task> requestCallb /// The topic to send the request in /// The typed request message to send /// An override to specify how long this request will live for. If null is given, then expiry will be taken from either T or TR attributed options + /// (optional) Crypto Encoding options /// The request type /// The response type /// The id of the request sent - Task SendRequest(string topic, T parameters, long? expiry = null); + Task SendRequest(string topic, T parameters, long? expiry = null, EncodeOptions options = null); /// /// Send a typed response message with the given request / response type pair T, TR to the given topic @@ -91,9 +93,10 @@ void HandleMessageType(Func, Task> requestCallb /// The id of the request to respond to /// The topic to send the response in /// The typed response message to send + /// (optional) Crypto Encoding options /// The request type /// The response type - Task SendResult(long id, string topic, TR result); + Task SendResult(long id, string topic, TR result, EncodeOptions options = null); /// /// Send an error response message with the given request / response type pair T, TR to the given topic @@ -101,8 +104,9 @@ void HandleMessageType(Func, Task> requestCallb /// The id of the request to respond to /// The topic to send the response in /// The error response to send + /// (optional) Crypto Encoding options /// The request type /// The response type - Task SendError(long id, string topic, ErrorResponse error); + Task SendError(long id, string topic, ErrorResponse error, EncodeOptions options = null); } } diff --git a/WalletConnectSharp.Core/Models/Eth/EthCall.cs b/WalletConnectSharp.Core/Models/Eth/EthCall.cs new file mode 100644 index 0000000..46a3ff3 --- /dev/null +++ b/WalletConnectSharp.Core/Models/Eth/EthCall.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace WalletConnectSharp.Core.Models.Eth; + +public class EthCall +{ + [JsonProperty("to")] + public string To { get; set; } + + [JsonProperty("data")] + public string Data { get; set; } +} diff --git a/WalletConnectSharp.Core/Models/Verify/Validation.cs b/WalletConnectSharp.Core/Models/Verify/Validation.cs new file mode 100644 index 0000000..cab41aa --- /dev/null +++ b/WalletConnectSharp.Core/Models/Verify/Validation.cs @@ -0,0 +1,8 @@ +namespace WalletConnectSharp.Core.Models.Verify; + +public static class Validation +{ + public const string Unknown = "UNKNOWN"; + public const string Valid = "VALID"; + public const string Invalid = "INVALID"; +} diff --git a/WalletConnectSharp.Core/Models/Verify/VerifiedContext.cs b/WalletConnectSharp.Core/Models/Verify/VerifiedContext.cs new file mode 100644 index 0000000..7b4530a --- /dev/null +++ b/WalletConnectSharp.Core/Models/Verify/VerifiedContext.cs @@ -0,0 +1,31 @@ +using Newtonsoft.Json; + +namespace WalletConnectSharp.Core.Models.Verify; + +public class VerifiedContext +{ + [JsonProperty("origin")] + public string Origin { get; set; } + + [JsonProperty("validation")] + private string _validation; + + public string Validation + { + get + { + return _validation; + } + set + { + if (value != Verify.Validation.Unknown && value != Verify.Validation.Invalid && + value != Verify.Validation.Valid) + throw new ArgumentException("Invalid validation value, must be one of Verify.Validation string"); + + _validation = value; + } + } + + [JsonProperty("verifyUrl")] + public string VerifyUrl { get; set; } +} diff --git a/WalletConnectSharp.Core/Models/Verify/Verifier.cs b/WalletConnectSharp.Core/Models/Verify/Verifier.cs new file mode 100644 index 0000000..ceedfb3 --- /dev/null +++ b/WalletConnectSharp.Core/Models/Verify/Verifier.cs @@ -0,0 +1,28 @@ +using System.Net; +using Newtonsoft.Json; +using WalletConnectSharp.Common.Utils; + +namespace WalletConnectSharp.Core.Models.Verify; + +public class Verifier +{ + public const string VerifyServer = "https://verify.walletconnect.com"; + + public CancellationTokenSource CancellationTokenSource { get; } + + public Verifier() + { + this.CancellationTokenSource = new CancellationTokenSource(Clock.AsTimeSpan(Clock.FIVE_SECONDS)); + } + + public async Task Resolve(string attestationId) + { + using HttpClient client = new HttpClient(); + var url = $"{VerifyServer}/attestation/{attestationId}"; + var results = await client.GetStringAsync(url); + + var verifiedContext = JsonConvert.DeserializeObject(results); + + return verifiedContext != null ? verifiedContext.Origin : ""; + } +} diff --git a/WalletConnectSharp.Core/Utils.cs b/WalletConnectSharp.Core/Utils.cs new file mode 100644 index 0000000..e38d989 --- /dev/null +++ b/WalletConnectSharp.Core/Utils.cs @@ -0,0 +1,24 @@ +namespace WalletConnectSharp.Core; + +public static class Utils +{ + public static bool IsValidUrl(string url) + { + if (string.IsNullOrWhiteSpace(url)) return false; + + try + { + new Uri(url); + return true; + } + catch (Exception e) + { + return false; + } + } + + public static bool IsValidRequestExpiry(long expiry, long min, long max) + { + return expiry <= max && expiry >= min; + } +} diff --git a/WalletConnectSharp.Sign/Internals/EngineValidation.cs b/WalletConnectSharp.Sign/Internals/EngineValidation.cs index 5e45d14..a7ed42d 100644 --- a/WalletConnectSharp.Sign/Internals/EngineValidation.cs +++ b/WalletConnectSharp.Sign/Internals/EngineValidation.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json; using WalletConnectSharp.Common.Model.Errors; using WalletConnectSharp.Common.Utils; +using WalletConnectSharp.Core; using WalletConnectSharp.Network.Models; using WalletConnectSharp.Sign.Interfaces; using WalletConnectSharp.Sign.Models; @@ -96,24 +97,9 @@ async Task IsValidSessionOrPairingTopic(string topic) } } - private bool IsValidUrl(string url) - { - if (string.IsNullOrWhiteSpace(url)) return false; - - try - { - new Uri(url); - return true; - } - catch (Exception e) - { - return false; - } - } - Task IEnginePrivate.IsValidPair(string uri) { - if (!IsValidUrl(uri)) + if (!Utils.IsValidUrl(uri)) throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID, $"pair() uri: {uri}"); return Task.CompletedTask; } From a76c0e743fb25d31ee2b087b2da7d1fe0a9719ea Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Mon, 5 Jun 2023 12:23:27 -0400 Subject: [PATCH 03/36] feat: complete auth & unit tests --- Directory.Packages.props | 2 + .../AuthClientFixture.cs | 49 ++ .../AuthClientTest.cs | 525 ++++++++++++++++++ .../SignatureTest.cs | 47 ++ .../WalletConnectSharp.Auth.Tests.csproj | 29 + .../SignClientFixture.cs | 40 +- .../TwoClientsFixture.cs | 62 --- .../TwoClientsFixture.cs | 21 + WalletConnectSharp.Auth/AuthClient.cs | 25 +- WalletConnectSharp.Auth/Cacao.cs | 76 ++- .../Controllers/AuthEngine.cs | 44 +- .../Interfaces/IAuthClient.cs | 10 +- .../Interfaces/IAuthEngine.cs | 2 +- .../Internals/AuthEngineValidations.cs | 2 +- .../Internals/SignatureUtils.cs | 11 +- WalletConnectSharp.Auth/Models/AuthPayload.cs | 1 - .../Models/RequestParams.cs | 16 + .../WalletConnectSharp.Auth.csproj | 4 + .../Controllers/JsonRpcHistory.cs | 6 +- .../Controllers/Publisher.cs | 1 + WalletConnectSharp.Core/Controllers/Store.cs | 4 +- .../Controllers/TypedMessageHandler.cs | 29 +- .../Interfaces/ITypedMessageHandler.cs | 19 + .../Models/Relay/RelayerEvents.cs | 5 + WalletConnectSharpV2.sln | 7 + 25 files changed, 936 insertions(+), 101 deletions(-) create mode 100644 Tests/WalletConnectSharp.Auth.Tests/AuthClientFixture.cs create mode 100644 Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs create mode 100644 Tests/WalletConnectSharp.Auth.Tests/SignatureTest.cs create mode 100644 Tests/WalletConnectSharp.Auth.Tests/WalletConnectSharp.Auth.Tests.csproj delete mode 100644 Tests/WalletConnectSharp.Sign.Test/TwoClientsFixture.cs create mode 100644 Tests/WalletConnectSharp.Tests.Common/TwoClientsFixture.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index bc4a080..4c09ad3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -11,5 +11,7 @@ + + \ No newline at end of file diff --git a/Tests/WalletConnectSharp.Auth.Tests/AuthClientFixture.cs b/Tests/WalletConnectSharp.Auth.Tests/AuthClientFixture.cs new file mode 100644 index 0000000..9a88df4 --- /dev/null +++ b/Tests/WalletConnectSharp.Auth.Tests/AuthClientFixture.cs @@ -0,0 +1,49 @@ +using WalletConnectSharp.Auth.Interfaces; +using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Storage; +using WalletConnectSharp.Tests.Common; + +namespace WalletConnectSharp.Auth.Tests; + +public class AuthClientFixture : TwoClientsFixture +{ + public AuthOptions OptionsA { get; protected set; } + + public AuthOptions OptionsB { get; protected set; } + + protected override async void Init() + { + OptionsA = new AuthOptions() + { + ProjectId = TestValues.TestProjectId, + RelayUrl = TestValues.TestRelayUrl, + Metadata = new Metadata() + { + Description = "An example dapp to showcase WalletConnectSharpv2", + Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, + Name = "WalletConnectSharpv2 Dapp Example", + Url = "https://walletconnect.com" + }, + // Omit if you want persistant storage + Storage = new InMemoryStorage(), + }; + + OptionsB = new AuthOptions() + { + ProjectId = TestValues.TestProjectId, + RelayUrl = TestValues.TestRelayUrl, + Metadata = new Metadata() + { + Description = "An example wallet to showcase WalletConnectSharpv2", + Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, + Name = "WalletConnectSharpv2 Wallet Example", + Url = "https://walletconnect.com" + }, + // Omit if you want persistant storage + Storage = new InMemoryStorage() + }; + + ClientA = await AuthClient.Init(OptionsA); + ClientB = await AuthClient.Init(OptionsB); + } +} diff --git a/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs b/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs new file mode 100644 index 0000000..0b19322 --- /dev/null +++ b/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs @@ -0,0 +1,525 @@ +using System.Text; +using NBitcoin; +using Nethereum.HdWallet; +using WalletConnectSharp.Auth.Interfaces; +using WalletConnectSharp.Auth.Internals; +using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Auth.Models.Engine; +using WalletConnectSharp.Core.Models.Pairing; +using WalletConnectSharp.Core.Models.Publisher; +using WalletConnectSharp.Core.Models.Relay; +using WalletConnectSharp.Core.Models.Verify; +using WalletConnectSharp.Events; +using Xunit; +using ErrorResponse = WalletConnectSharp.Auth.Models.ErrorResponse; + +namespace WalletConnectSharp.Auth.Tests +{ + public class AuthClientTests : IClassFixture + { + private static readonly RequestParams DefaultRequestParams = new RequestParams() + { + Aud = "http://localhost:3000/login", + Domain = "localhost:3000", + ChainId = "eip155:1", + Nonce = CryptoUtils.GenerateNonce() + }; + + private AuthClientFixture _authFixture; + private readonly string _iss; + private readonly Wallet _wallet; + + public IAuthClient PeerA + { + get + { + return _authFixture.ClientA; + } + } + + public IAuthClient PeerB + { + get + { + return _authFixture.ClientB; + } + } + + public string WalletAddress + { + get + { + return _wallet.GetAddresses(1)[0]; + } + } + + public AuthClientTests(AuthClientFixture authFixture) + { + this._authFixture = authFixture; + + this._wallet = new Wallet(Wordlist.English, WordCount.Twelve); + this._iss = $"did:pkh:eip155:1:{this.WalletAddress}"; + } + + [Fact, Trait("Category", "unit")] + public async void TestInit() + { + await _authFixture.WaitForClientsReady(); + + Assert.NotNull(PeerA); + Assert.NotNull(PeerB); + + Assert.NotNull(PeerA.Core); + Assert.NotNull(PeerA.Events); + Assert.NotNull(PeerA.Core.Expirer); + Assert.NotNull(PeerA.Core.History); + Assert.NotNull(PeerA.Core.Pairing); + + Assert.NotNull(PeerB.Core); + Assert.NotNull(PeerB.Events); + Assert.NotNull(PeerB.Core.Expirer); + Assert.NotNull(PeerB.Core.History); + Assert.NotNull(PeerB.Core.Pairing); + } + + [Fact, Trait("Category", "unit")] + public async void TestPairs() + { + await _authFixture.WaitForClientsReady(); + + var ogPairSize = PeerA.Core.Pairing.Pairings.Length; + + TaskCompletionSource authRequested = new TaskCompletionSource(); + + void OnPeerBOnAuthRequested(object o, AuthRequest authRequest) => authRequested.SetResult(true); + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + var uriData = await PeerA.Request(DefaultRequestParams); + var uri = uriData.Uri; + + await PeerB.Core.Pairing.Pair(uri); + + await authRequested.Task; + + Assert.Equal(PeerA.Core.Pairing.Pairings.Select(p => p.Key), PeerB.Core.Pairing.Pairings.Select(p => p.Key)); + Assert.Equal(ogPairSize + 1, PeerA.Core.Pairing.Pairings.Length); + + var peerAHistory = await PeerA.Core.History.JsonRpcHistoryOfType(); + var peerBHistory = await PeerB.Core.History.JsonRpcHistoryOfType(); + + Assert.Equal(peerAHistory.Size, peerBHistory.Size); + + Assert.True(PeerB.Core.Pairing.Pairings[0].Active); + + // Cleanup event listeners + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + } + + [Fact, Trait("Category", "unit")] + public async void TestKnownPairings() + { + await _authFixture.WaitForClientsReady(); + + var ogSizeA = PeerA.Core.Pairing.Pairings.Length; + var history = await PeerA.AuthHistory(); + var ogHistorySizeA = history.Keys.Length; + + var ogSizeB = PeerB.Core.Pairing.Pairings.Length; + var historyB = await PeerB.AuthHistory(); + var ogHistorySizeB = historyB.Keys.Length; + + List responses = new List(); + TaskCompletionSource responseTask = new TaskCompletionSource(); + + async void OnPeerBOnAuthRequested(object sender, AuthRequest request) + { + var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this._iss); + var signature = await _wallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); + + await PeerB.Respond(new Cacao() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this._iss); + + Assert.Equal(Validation.Unknown, request.VerifyContext?.Validation); + } + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + void OnPeerAOnAuthResponded(object sender, AuthResponse args) + { + responses.Add(args); + responseTask.SetResult(args); + } + + PeerA.AuthResponded += OnPeerAOnAuthResponded; + + void OnPeerAOnAuthError(object sender, AuthErrorResponse args) + { + responses.Add(args); + responseTask.SetResult(args); + } + + PeerA.AuthError += OnPeerAOnAuthError; + + var requestData = await PeerA.Request(DefaultRequestParams); + + await PeerB.Core.Pairing.Pair(requestData.Uri); + + await responseTask.Task; + + // Reset + responseTask = new TaskCompletionSource(); + + // Get last pairing, that is the one we just made + var knownPairing = PeerA.Core.Pairing.Pairings[^1]; + + var requestData2 = await PeerA.Request(DefaultRequestParams, knownPairing.Topic); + + await responseTask.Task; + + Assert.Null(requestData2.Uri); + + Assert.Equal(ogSizeA + 1, PeerA.Core.Pairing.Pairings.Length); + Assert.Equal(ogHistorySizeA + 2, history.Keys.Length); + Assert.Equal(ogSizeB + 1, PeerB.Core.Pairing.Pairings.Length); + Assert.Equal(ogHistorySizeB + 2, historyB.Keys.Length); + Assert.Equal(responses[0].Topic, responses[1].Topic); + + // Cleanup event listeners + + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + PeerA.AuthResponded -= OnPeerAOnAuthResponded; + PeerA.AuthError -= OnPeerAOnAuthError; + } + + [Fact, Trait("Category", "unit")] + public async void HandlesAuthRequests() + { + await _authFixture.WaitForClientsReady(); + + var ogSize = PeerB.Requests.Length; + + TaskCompletionSource receivedAuthRequest = new TaskCompletionSource(); + + void OnPeerBOnAuthRequested(object o, AuthRequest authRequest) => receivedAuthRequest.SetResult(true); + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + var requestData = await PeerA.Request(DefaultRequestParams); + + await PeerB.Core.Pairing.Pair(requestData.Uri); + + await receivedAuthRequest.Task; + + Assert.Equal(ogSize + 1, PeerB.Requests.Length); + + // Cleanup event listeners + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + } + + [Fact, Trait("Category", "unit")] + public async void TestErrorResponses() + { + await _authFixture.WaitForClientsReady(); + + var ogPSize = PeerA.Core.Pairing.Pairings.Length; + + TaskCompletionSource errorResponse = new TaskCompletionSource(); + + async void OnPeerBOnAuthRequested(object sender, AuthRequest request) + { + await PeerB.Respond(new ErrorResponse() { Error = new Network.Models.ErrorResponse() { Code = 14001, Message = "Can not login" }, Id = request.Id }, this._iss); + } + void OnPeerAOnAuthResponded(object sender, AuthResponse response) + { + errorResponse.SetResult(false); + } + + void OnPeerAOnAuthError(object sender, AuthErrorResponse response) => errorResponse.SetResult(true); + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + PeerA.AuthResponded += OnPeerAOnAuthResponded; + PeerA.AuthError += OnPeerAOnAuthError; + + var requestData = await PeerA.Request(DefaultRequestParams); + + Assert.Equal(ogPSize + 1, PeerA.Core.Pairing.Pairings.Length); + Assert.False(PeerA.Core.Pairing.Pairings[^1].Active); + + await PeerB.Core.Pairing.Pair(requestData.Uri); + + await errorResponse.Task; + + Assert.False(PeerA.Core.Pairing.Pairings[^1].Active); + Assert.True(errorResponse.Task.Result); + + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + PeerA.AuthResponded -= OnPeerAOnAuthResponded; + PeerA.AuthError -= OnPeerAOnAuthError; + } + + [Fact, Trait("Category", "unit")] + public async void HandlesSuccessfulResponse() + { + await _authFixture.WaitForClientsReady(); + + var ogPSize = PeerA.Core.Pairing.Pairings.Length; + + TaskCompletionSource successfulResponse = new TaskCompletionSource(); + + async void OnPeerBOnAuthRequested(object sender, AuthRequest request) + { + var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this._iss); + var signature = await _wallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); + + await PeerB.Respond(new ResultResponse() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this._iss); + + Assert.Equal(Validation.Unknown, request.VerifyContext?.Validation); + } + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + void OnPeerAOnAuthResponded(object sender, AuthResponse response) => successfulResponse.SetResult(response.Response.Result?.Signature != null); + + PeerA.AuthResponded += OnPeerAOnAuthResponded; + + void OnPeerAOnAuthError(object sender, AuthErrorResponse response) => successfulResponse.SetResult(false); + + PeerA.AuthError += OnPeerAOnAuthError; + + var requestData = await PeerA.Request(DefaultRequestParams); + + Assert.Equal(ogPSize + 1, PeerA.Core.Pairing.Pairings.Length); + Assert.False(PeerA.Core.Pairing.Pairings[^1].Active); + + await PeerB.Core.Pairing.Pair(requestData.Uri); + + await successfulResponse.Task; + + Assert.True(PeerA.Core.Pairing.Pairings[^1].Active); + Assert.True(successfulResponse.Task.Result); + + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + PeerA.AuthResponded -= OnPeerAOnAuthResponded; + PeerA.AuthError -= OnPeerAOnAuthError; + } + + [Fact, Trait("Category", "unit")] + public async void TestCustomRequestExpiry() + { + await _authFixture.WaitForClientsReady(); + + var uri = ""; + var expiry = 1000; + + TaskCompletionSource resolve1 = new TaskCompletionSource(); + + PeerA.Core.Relayer.Once(RelayerEvents.Publish, (sender, @event) => + { + Assert.Equal(expiry, @event.EventData.Options?.TTL); + resolve1.SetResult(true); + }); + + + await Task.WhenAll(resolve1.Task, Task.Run(async () => + { + var response = await PeerA.Request(new RequestParams(DefaultRequestParams) { Expiry = expiry }); + uri = response.Uri; + })); + + TaskCompletionSource resolve3 = new TaskCompletionSource(); + + async void OnPeerBOnAuthRequested(object sender, AuthRequest request) + { + var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this._iss); + var signature = await _wallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); + + await PeerB.Respond(new ResultResponse() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this._iss); + resolve3.SetResult(true); + } + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + await Task.WhenAll(resolve3.Task, PeerB.Core.Pairing.Pair(uri)); + + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + } + + [Fact, Trait("Category", "unit")] + public async void TestGetPendingPairings() + { + await _authFixture.WaitForClientsReady(); + + var ogCount = PeerB.PendingRequests.Count; + + TaskCompletionSource receivedAuthRequest = new TaskCompletionSource(); + var aud = "http://localhost:3000/login"; + + void OnPeerBOnAuthRequested(object sender, AuthRequest request) => receivedAuthRequest.SetResult(true); + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + var requestData = await PeerA.Request(DefaultRequestParams); + + await PeerB.Core.Pairing.Pair(requestData.Uri); + + await receivedAuthRequest.Task; + + var requests = PeerB.PendingRequests; + + Assert.Equal(ogCount + 1, requests.Count); + Assert.Contains(requests, r => r.Value.CacaoPayload.Aud == aud); + + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + } + + [Fact, Trait("Category", "unit")] + public async void TestGetPairings() + { + var peerAOgSize = PeerA.Core.Pairing.Pairings.Length; + var peerBOgSize = PeerB.Core.Pairing.Pairings.Length; + + TaskCompletionSource receivedAuthRequest = new TaskCompletionSource(); + + void OnPeerBOnAuthRequested(object sender, AuthRequest request) => receivedAuthRequest.SetResult(true); + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + var requestData = await PeerA.Request(DefaultRequestParams); + + await PeerB.Core.Pairing.Pair(requestData.Uri); + + await receivedAuthRequest.Task; + + var clientPairings = PeerA.Core.Pairing.Pairings; + var peerPairings = PeerB.Core.Pairing.Pairings; + + Assert.Equal(peerAOgSize + 1, clientPairings.Length); + Assert.Equal(peerBOgSize + 1, peerPairings.Length); + Assert.Equal(clientPairings[^1].Topic, peerPairings[^1].Topic); + + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + } + + [Fact, Trait("Category", "unit")] + public async void TestPing() + { + await _authFixture.WaitForClientsReady(); + + TaskCompletionSource receivedAuthRequest = new TaskCompletionSource(); + TaskCompletionSource receivedClientPing = new TaskCompletionSource(); + TaskCompletionSource receivedPeerPing = new TaskCompletionSource(); + + void OnPeerBOnAuthRequested(object sender, AuthRequest request) => receivedAuthRequest.SetResult(true); + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + PeerB.Core.Pairing.Once(PairingEvents.PairingPing, (sender, @event) => + { + receivedPeerPing.SetResult(true); + }); + + PeerA.Core.Pairing.Once(PairingEvents.PairingPing, (sender, @event) => + { + receivedClientPing.SetResult(true); + }); + + var requestData = await PeerA.Request(DefaultRequestParams); + + await PeerB.Core.Pairing.Pair(requestData.Uri); + + await receivedAuthRequest.Task; + + var pairing = PeerA.Core.Pairing.Pairings[^1]; + await PeerA.Core.Pairing.Ping(pairing.Topic); + await PeerB.Core.Pairing.Ping(pairing.Topic); + + await Task.WhenAll(receivedClientPing.Task, receivedPeerPing.Task); + + Assert.True(receivedClientPing.Task.Result); + Assert.True(receivedPeerPing.Task.Result); + + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + } + + [Fact, Trait("Category", "unit")] + public async void TestDisconnectedPairing() + { + await _authFixture.WaitForClientsReady(); + + var peerAOgSize = PeerA.Core.Pairing.Pairings.Length; + var peerBOgSize = PeerB.Core.Pairing.Pairings.Length; + + TaskCompletionSource receivedAuthRequest = new TaskCompletionSource(); + TaskCompletionSource peerDeletedPairing = new TaskCompletionSource(); + + void OnPeerBOnAuthRequested(object sender, AuthRequest request) => receivedAuthRequest.SetResult(true); + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + PeerB.Core.Pairing.Once(PairingEvents.PairingDelete, (sender, @event) => + { + peerDeletedPairing.SetResult(true); + }); + + var requestData = await PeerA.Request(DefaultRequestParams); + + await PeerB.Core.Pairing.Pair(requestData.Uri); + + await receivedAuthRequest.Task; + + var clientPairings = PeerA.Core.Pairing.Pairings; + var peerPairings = PeerB.Core.Pairing.Pairings; + + Assert.Equal(peerAOgSize + 1, PeerA.Core.Pairing.Pairings.Length); + Assert.Equal(peerBOgSize + 1, PeerB.Core.Pairing.Pairings.Length); + Assert.Equal(clientPairings[^1].Topic, peerPairings[^1].Topic); + + await PeerA.Core.Pairing.Disconnect(clientPairings[^1].Topic); + + await peerDeletedPairing.Task; + Assert.Equal(peerAOgSize, PeerA.Core.Pairing.Pairings.Length); + Assert.Equal(peerBOgSize, PeerB.Core.Pairing.Pairings.Length); + + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + } + + [Fact, Trait("Category", "unit")] + public async void TestReceivesMetadata() + { + await _authFixture.WaitForClientsReady(); + + var receivedMetadataName = ""; + var ogPairingSize = PeerA.Core.Pairing.Pairings.Length; + + TaskCompletionSource hasResponded = new TaskCompletionSource(); + + async void OnPeerBOnAuthRequested(object sender, AuthRequest request) + { + receivedMetadataName = request.Parameters.Requester?.Metadata?.Name; + var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this._iss); + var signature = await _wallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); + + await PeerB.Respond(new ResultResponse() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this._iss); + + hasResponded.SetResult(true); + Assert.Equal(Validation.Unknown, request.VerifyContext.Validation); + } + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + var requestData = await PeerA.Request(DefaultRequestParams); + + Assert.Equal(ogPairingSize + 1, PeerA.Core.Pairing.Pairings.Length); + Assert.False(PeerA.Core.Pairing.Pairings[^1].Active); + + await PeerB.Core.Pairing.Pair(requestData.Uri); + + await hasResponded.Task; + + Assert.True(PeerA.Core.Pairing.Pairings[^1].Active); + Assert.True(hasResponded.Task.Result); + Assert.Equal(PeerA.Metadata.Name, receivedMetadataName); + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + } + } +} diff --git a/Tests/WalletConnectSharp.Auth.Tests/SignatureTest.cs b/Tests/WalletConnectSharp.Auth.Tests/SignatureTest.cs new file mode 100644 index 0000000..4ba407d --- /dev/null +++ b/Tests/WalletConnectSharp.Auth.Tests/SignatureTest.cs @@ -0,0 +1,47 @@ +using WalletConnectSharp.Auth.Internals; +using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Tests.Common; +using Xunit; + +namespace WalletConnectSharp.Auth.Tests; + +public class SignatureTest +{ + public string ChainId = "eip155:1"; + public string ProjectId = TestValues.TestProjectId; + public string Address = "0x2faf83c542b68f1b4cdc0e770e8cb9f567b08f71"; + + public string ReconstructedMessage = @"localhost wants you to sign in with your Ethereum account: +0x2faf83c542b68f1b4cdc0e770e8cb9f567b08f71 + +URI: http://localhost:3000/ +Version: 1 +Chain ID: 1 +Nonce: 1665443015700 +Issued At: 2022-10-10T23:03:35.700Z +Expiration Time: 2022-10-11T23:03:35.700Z".Replace("\r", ""); + + [Fact, Trait("Category", "unit")] + public async void TestValidEip1271Signature() + { + var signature = new Cacao.CacaoSignature.EIP1271CacaoSignature( + "0xc1505719b2504095116db01baaf276361efd3a73c28cf8cc28dabefa945b8d536011289ac0a3b048600c1e692ff173ca944246cf7ceb319ac2262d27b395c82b1c"); + + var isValid = + await SignatureUtils.VerifySignature(Address, ReconstructedMessage, signature, ChainId, ProjectId); + + Assert.True(isValid); + } + + [Fact, Trait("Category", "unit")] + public async void TestBadEip1271Signature() + { + var signature = new Cacao.CacaoSignature.EIP1271CacaoSignature( + "0xdead5719b2504095116db01baaf276361efd3a73c28cf8cc28dabefa945b8d536011289ac0a3b048600c1e692ff173ca944246cf7ceb319ac2262d27b395c82b1c"); + + var isValid = + await SignatureUtils.VerifySignature(Address, ReconstructedMessage, signature, ChainId, ProjectId); + + Assert.False(isValid); + } +} diff --git a/Tests/WalletConnectSharp.Auth.Tests/WalletConnectSharp.Auth.Tests.csproj b/Tests/WalletConnectSharp.Auth.Tests/WalletConnectSharp.Auth.Tests.csproj new file mode 100644 index 0000000..f181227 --- /dev/null +++ b/Tests/WalletConnectSharp.Auth.Tests/WalletConnectSharp.Auth.Tests.csproj @@ -0,0 +1,29 @@ + + + + net6.0;netcoreapp3.1 + 2.0.0 + 2.0.0 + 2.0.0 + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/Tests/WalletConnectSharp.Sign.Test/SignClientFixture.cs b/Tests/WalletConnectSharp.Sign.Test/SignClientFixture.cs index bcc9c13..78cf6c9 100644 --- a/Tests/WalletConnectSharp.Sign.Test/SignClientFixture.cs +++ b/Tests/WalletConnectSharp.Sign.Test/SignClientFixture.cs @@ -1,9 +1,47 @@ -namespace WalletConnectSharp.Sign.Test; +using WalletConnectSharp.Core.Models.Pairing; +using WalletConnectSharp.Sign.Models; +using WalletConnectSharp.Storage; +using WalletConnectSharp.Tests.Common; + +namespace WalletConnectSharp.Sign.Test; public class SignClientFixture : TwoClientsFixture { + public SignClientOptions OptionsA { get; protected set; } + public SignClientOptions OptionsB { get; protected set; } + protected override async void Init() { + OptionsA = new SignClientOptions() + { + ProjectId = TestValues.TestProjectId, + RelayUrl = TestValues.TestRelayUrl, + Metadata = new Metadata() + { + Description = "An example dapp to showcase WalletConnectSharpv2", + Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, + Name = "WalletConnectSharpv2 Dapp Example", + Url = "https://walletconnect.com" + }, + // Omit if you want persistant storage + Storage = new InMemoryStorage() + }; + + OptionsB = new SignClientOptions() + { + ProjectId = TestValues.TestProjectId, + RelayUrl = TestValues.TestRelayUrl, + Metadata = new Metadata() + { + Description = "An example wallet to showcase WalletConnectSharpv2", + Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, + Name = "WalletConnectSharpv2 Wallet Example", + Url = "https://walletconnect.com" + }, + // Omit if you want persistant storage + Storage = new InMemoryStorage() + }; + ClientA = await WalletConnectSignClient.Init(OptionsA); ClientB = await WalletConnectSignClient.Init(OptionsB); } diff --git a/Tests/WalletConnectSharp.Sign.Test/TwoClientsFixture.cs b/Tests/WalletConnectSharp.Sign.Test/TwoClientsFixture.cs deleted file mode 100644 index 7771824..0000000 --- a/Tests/WalletConnectSharp.Sign.Test/TwoClientsFixture.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Threading.Tasks; -using WalletConnectSharp.Core.Models; -using WalletConnectSharp.Core.Models.Pairing; -using WalletConnectSharp.Sign.Interfaces; -using WalletConnectSharp.Sign.Models; -using WalletConnectSharp.Storage; -using WalletConnectSharp.Tests.Common; - -namespace WalletConnectSharp.Sign.Test -{ - public abstract class TwoClientsFixture where SClient : ISignClient - { - public SClient ClientA { get; protected set; } - public SClient ClientB { get; protected set; } - - public SignClientOptions OptionsA { get; } - public SignClientOptions OptionsB { get; } - - public TwoClientsFixture() - { - OptionsA = new SignClientOptions() - { - ProjectId = TestValues.TestProjectId, - RelayUrl = TestValues.TestRelayUrl, - Metadata = new Metadata() - { - Description = "An example dapp to showcase WalletConnectSharpv2", - Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, - Name = "WalletConnectSharpv2 Dapp Example", - Url = "https://walletconnect.com" - }, - // Omit if you want persistant storage - Storage = new InMemoryStorage() - }; - - OptionsB = new SignClientOptions() - { - ProjectId = TestValues.TestProjectId, - RelayUrl = TestValues.TestRelayUrl, - Metadata = new Metadata() - { - Description = "An example wallet to showcase WalletConnectSharpv2", - Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, - Name = "WalletConnectSharpv2 Wallet Example", - Url = "https://walletconnect.com" - }, - // Omit if you want persistant storage - Storage = new InMemoryStorage() - }; - - Init(); - } - - protected abstract void Init(); - - public async Task WaitForClientsReady() - { - while (ClientA == null || ClientB == null) - await Task.Delay(10); - } - } -} diff --git a/Tests/WalletConnectSharp.Tests.Common/TwoClientsFixture.cs b/Tests/WalletConnectSharp.Tests.Common/TwoClientsFixture.cs new file mode 100644 index 0000000..0061649 --- /dev/null +++ b/Tests/WalletConnectSharp.Tests.Common/TwoClientsFixture.cs @@ -0,0 +1,21 @@ +namespace WalletConnectSharp.Tests.Common; + +public abstract class TwoClientsFixture +{ + public TClient ClientA { get; protected set; } + public TClient ClientB { get; protected set; } + + + public TwoClientsFixture() + { + Init(); + } + + protected abstract void Init(); + + public async Task WaitForClientsReady() + { + while (ClientA == null || ClientB == null) + await Task.Delay(10); + } +} diff --git a/WalletConnectSharp.Auth/AuthClient.cs b/WalletConnectSharp.Auth/AuthClient.cs index 8f7bdbe..64d6c20 100644 --- a/WalletConnectSharp.Auth/AuthClient.cs +++ b/WalletConnectSharp.Auth/AuthClient.cs @@ -1,6 +1,7 @@ using WalletConnectSharp.Auth.Controllers; using WalletConnectSharp.Auth.Interfaces; using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Auth.Models.Engine; using WalletConnectSharp.Core.Controllers; using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Events; @@ -18,7 +19,7 @@ public string Name { get { - return AUTH_CLIENT_DEFAULT_NAME; + return $"{Metadata.Name}-{AUTH_CLIENT_DEFAULT_NAME}"; } } @@ -57,6 +58,12 @@ public int Version public IStore AuthKeys { get; set; } public IStore PairingTopics { get; set; } public IStore Requests { get; set; } + + public Task> AuthHistory() + { + return Core.History.JsonRpcHistoryOfType(); + } + public IAuthEngine Engine { get; } public AuthOptions Options { get; } @@ -68,7 +75,7 @@ public IDictionary PendingRequests } } - public static async Task Init(AuthOptions options) + public static async Task Init(AuthOptions options) { var client = new AuthClient(options); await client.Initialize(); @@ -89,6 +96,10 @@ private AuthClient(AuthOptions options) Options = options; Metadata = options.Metadata; ProjectId = options.ProjectId; + + if (string.IsNullOrWhiteSpace(options.Name)) + options.Name = $"{options.Metadata.Name}-{Name}"; + Core = options.Core ?? new Core.Core(options); AuthKeys = new Store(Core, "authKeys", AUTH_CLIENT_STORAGE_PREFIX); @@ -96,6 +107,7 @@ private AuthClient(AuthOptions options) Requests = new Store(Core, "requests", AUTH_CLIENT_STORAGE_PREFIX); Engine = new AuthEngine(this); + Events = new EventDelegator(this); } public Task Request(RequestParams @params, string topic = null) @@ -108,9 +120,14 @@ public Task Respond(Message message, string iss) return this.Engine.Respond(message, iss); } - public string FormatMessage(Cacao.CacaoPayload cacao, string iss) + public string FormatMessage(Cacao.CacaoPayload cacao) + { + return this.Engine.FormatMessage(cacao); + } + + public string FormatMessage(Cacao.CacaoRequestPayload cacao, string iss) { - return this.Engine.FormatMessage(cacao, iss); + return FormatMessage(new Cacao.CacaoPayload(cacao, iss)); } bool IAuthClient.OnAuthRequest(AuthRequest request) diff --git a/WalletConnectSharp.Auth/Cacao.cs b/WalletConnectSharp.Auth/Cacao.cs index b516bdc..527d13a 100644 --- a/WalletConnectSharp.Auth/Cacao.cs +++ b/WalletConnectSharp.Auth/Cacao.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using WalletConnectSharp.Common.Utils; using WalletConnectSharp.Network.Models; @@ -58,6 +59,10 @@ public class CacaoPayload : CacaoRequestPayload [JsonProperty("iss")] public string Iss { get; set; } + public CacaoPayload() + { + } + public CacaoPayload(CacaoRequestPayload source) { this.Aud = source.Aud; @@ -91,28 +96,87 @@ public CacaoPayload(CacaoRequestPayload source, string iss) } } + [JsonConverter(typeof(CacaoSignatureConverter))] public abstract class CacaoSignature { [JsonProperty("t")] - public readonly string T; + public abstract string T { get; } public class EIP191CacaoSignature : CacaoSignature { + public EIP191CacaoSignature(string signature) : base(signature) + { + } + [JsonProperty("t")] - public readonly string T = "eip191"; + public override string T + { + get + { + return "eip191"; + } + } } public class EIP1271CacaoSignature : CacaoSignature { + [JsonProperty("m")] + public string M { get; set; } + + public EIP1271CacaoSignature(string signature) : base(signature) + { + } + [JsonProperty("t")] - public readonly string T = "eip1271"; + public override string T + { + get + { + return "eip1271"; + } + } } [JsonProperty("s")] public string S { get; set; } - - [JsonProperty("m")] - public string M { get; set; } + + protected CacaoSignature(string signature) + { + S = signature; + } + } + + public class CacaoSignatureConverter : JsonConverter + { + public override bool CanWrite + { + get + { + return false; + } + } + + public override void WriteJson(JsonWriter writer, CacaoSignature value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override CacaoSignature ReadJson(JsonReader reader, Type objectType, CacaoSignature existingValue, bool hasExistingValue, + JsonSerializer serializer) + { + JObject jsonObject = JObject.Load(reader); + var type = jsonObject.Value("t"); + var sig = jsonObject.Value("s"); + return type switch + { + "eip191" => new CacaoSignature.EIP191CacaoSignature(sig), + "eip1271" => new CacaoSignature.EIP1271CacaoSignature(sig) + { + M = jsonObject.Value("m") + }, + _ => throw new ArgumentException($"Invalid type {type}, expected eip191 or eip1271") + }; + } } [JsonProperty("h")] diff --git a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs index a755b69..1590cef 100644 --- a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs +++ b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs @@ -74,6 +74,8 @@ public async Task Request(RequestParams @params, string topic = null { IsInitialized(); + @params.Type ??= new Cacao.CacaoHeader(); + if (!IsValidRequest(@params)) { throw new ArgumentException("Invalid request", nameof(@params)); @@ -98,6 +100,11 @@ await this.Client.AuthKeys.Set(AUTH_CLIENT_PUBLIC_KEY_NAME, await this.Client.PairingTopics.Set(responseTopic, new PairingData() { Topic = responseTopic, PairingTopic = pairingTopic }); + this.Client.Core.MessageHandler.SetDecodeOptionsForTopic(new DecodeOptions() + { + ReceiverPublicKey = publicKey + }, responseTopic); + await this.Client.Core.Relayer.Subscribe(responseTopic); // TODO Log @@ -176,18 +183,28 @@ public async Task Respond(Message message, string iss) Type = Crypto.Crypto.TYPE_1, ReceiverPublicKey = receiverPublicKey, SenderPublicKey = senderPublicKey }; - if (message is ErrorResponse errorResponse) + Cacao cacao; + switch (message) { - await this.SendError(id, responseTopic, errorResponse, encodeOptions); - return; + case ErrorResponse errorResponse: + await this.SendError(id, responseTopic, errorResponse, encodeOptions); + return; + case Cacao cacao1: + cacao = cacao1; + cacao.Payload ??= new Cacao.CacaoPayload(pendingRequest.CacaoPayload) { Iss = iss }; + break; + case ResultResponse response: + cacao = new Cacao() + { + Payload = new Cacao.CacaoPayload(pendingRequest.CacaoPayload) { Iss = iss }, + Signature = response.Signature + }; + break; + default: + throw new ArgumentException( + $"Unknown message type {message.GetType()}, expected Cacao or ResultResponse"); } - var cacao = new Cacao() - { - Payload = new Cacao.CacaoPayload(pendingRequest.CacaoPayload) { Iss = iss }, - Signature = ((ResultResponse)message).Signature - }; - await this.SendResult(id, responseTopic, cacao, encodeOptions); await this.Client.Requests.Update(id, cacao); @@ -229,12 +246,14 @@ private async Task OnAuthResponse(string topic, JsonRpcResponse response) if (!response.IsError) { - var pairingTopic = this.Client.PairingTopics.Get(topic); + var pairing = this.Client.PairingTopics.Get(topic); + await this.Client.Core.Pairing.Activate(pairing.PairingTopic); + var signature = response.Result.Signature; var payload = response.Result.Payload; await this.Client.Requests.Set(id, response.Result); - var reconstructed = FormatMessage(payload, payload.Iss); + var reconstructed = FormatMessage(payload); // TODO Log @@ -341,8 +360,9 @@ private async Task GetVerifyContext(string hash, Metadata metad return context; } - public string FormatMessage(Cacao.CacaoPayload cacao, string iss) + public string FormatMessage(Cacao.CacaoPayload cacao) { + string iss = cacao.Iss; var header = $"{cacao.Domain} wants you to sign in with your Ethereum account:"; var walletAddress = IssDidUtils.DidAddress(iss); var statement = cacao.Statement; diff --git a/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs b/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs index f5a3047..4789a37 100644 --- a/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs +++ b/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs @@ -1,4 +1,5 @@ using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Auth.Models.Engine; using WalletConnectSharp.Common; using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Events.Interfaces; @@ -20,7 +21,7 @@ public interface IAuthClient : IModule, IEvents IStore AuthKeys { get; set; } IStore PairingTopics { get; set; } IStore Requests { get; set; } - + IAuthEngine Engine { get; } AuthOptions Options { get; } @@ -31,7 +32,12 @@ public interface IAuthClient : IModule, IEvents Task Respond(Message message, string iss); - string FormatMessage(Cacao.CacaoPayload cacao, string iss); + Task> AuthHistory(); + + string FormatMessage(Cacao.CacaoPayload cacao); + + + string FormatMessage(Cacao.CacaoRequestPayload cacao, string iss); internal bool OnAuthRequest(AuthRequest request); diff --git a/WalletConnectSharp.Auth/Interfaces/IAuthEngine.cs b/WalletConnectSharp.Auth/Interfaces/IAuthEngine.cs index 190c77c..01df8d2 100644 --- a/WalletConnectSharp.Auth/Interfaces/IAuthEngine.cs +++ b/WalletConnectSharp.Auth/Interfaces/IAuthEngine.cs @@ -15,5 +15,5 @@ public interface IAuthEngine : IModule Task Respond(Message message, string iss); - string FormatMessage(Cacao.CacaoPayload cacao, string iss); + string FormatMessage(Cacao.CacaoPayload cacao); } diff --git a/WalletConnectSharp.Auth/Internals/AuthEngineValidations.cs b/WalletConnectSharp.Auth/Internals/AuthEngineValidations.cs index bd3b476..93f7a07 100644 --- a/WalletConnectSharp.Auth/Internals/AuthEngineValidations.cs +++ b/WalletConnectSharp.Auth/Internals/AuthEngineValidations.cs @@ -22,7 +22,7 @@ internal bool IsValidRequest(RequestParams @params) var hasNonce = !string.IsNullOrWhiteSpace(@params.Nonce); var hasValidType = @params.Type is { t: "eip4361" }; var expiry = @params.Expiry; - if (expiry != null && Utils.IsValidRequestExpiry(expiry.Value, MinExpiry, MaxExpiry)) + 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}"); } diff --git a/WalletConnectSharp.Auth/Internals/SignatureUtils.cs b/WalletConnectSharp.Auth/Internals/SignatureUtils.cs index 1b13ac8..6c5b48c 100644 --- a/WalletConnectSharp.Auth/Internals/SignatureUtils.cs +++ b/WalletConnectSharp.Auth/Internals/SignatureUtils.cs @@ -1,4 +1,6 @@ -using Newtonsoft.Json; +using System.Text; +using Nethereum.Signer; +using Newtonsoft.Json; using WalletConnectSharp.Auth.Models; using WalletConnectSharp.Common.Utils; using WalletConnectSharp.Core.Models.Eth; @@ -31,7 +33,8 @@ private static async Task IsValidEip1271Signature(string address, string r var dynamicTypeOffset = "0000000000000000000000000000000000000000000000000000000000000040"; var dynamicTypeLength = "0000000000000000000000000000000000000000000000000000000000000041"; var nonPrefixedSignature = cacaoSignatureS.Substring(2); - var nonPrefixedHashedMessage = HashUtils.HashMessage(reconstructedMessage).Substring(2); + var signer = new EthereumMessageSigner(); + var nonPrefixedHashedMessage = signer.HashPrefixedMessage(Encoding.UTF8.GetBytes(reconstructedMessage)).ToHex(); var data = eip1271MagicValue + @@ -71,6 +74,8 @@ private static async Task IsValidEip1271Signature(string address, string r private static bool IsValidEip191Signature(string address, string reconstructedMessage, string cacaoSignatureS) { - return false; + var signer = new EthereumMessageSigner(); + var recoveredAddress = signer.EncodeUTF8AndEcRecover(reconstructedMessage, cacaoSignatureS); + return String.Equals(recoveredAddress, address, StringComparison.CurrentCultureIgnoreCase); } } diff --git a/WalletConnectSharp.Auth/Models/AuthPayload.cs b/WalletConnectSharp.Auth/Models/AuthPayload.cs index 1e23843..9ef7d26 100644 --- a/WalletConnectSharp.Auth/Models/AuthPayload.cs +++ b/WalletConnectSharp.Auth/Models/AuthPayload.cs @@ -33,5 +33,4 @@ public class AuthPayload [JsonProperty("resources", NullValueHandling = NullValueHandling.Ignore)] public string[] Resources { get; set; } - } diff --git a/WalletConnectSharp.Auth/Models/RequestParams.cs b/WalletConnectSharp.Auth/Models/RequestParams.cs index 2025908..69ada36 100644 --- a/WalletConnectSharp.Auth/Models/RequestParams.cs +++ b/WalletConnectSharp.Auth/Models/RequestParams.cs @@ -6,4 +6,20 @@ public class RequestParams : AuthPayload { [JsonProperty("expiry", NullValueHandling = NullValueHandling.Ignore)] public long? Expiry { get; set; } + + public RequestParams() { } + + public RequestParams(AuthPayload payload) + { + this.Aud = payload.Aud; + this.Domain = payload.Domain; + this.Exp = payload.Exp; + this.Nbf = payload.Nbf; + this.Nonce = payload.Nonce; + this.Resources = payload.Resources; + this.Statement = payload.Statement; + this.Type = payload.Type; + this.ChainId = payload.ChainId; + this.RequestId = payload.RequestId; + } } diff --git a/WalletConnectSharp.Auth/WalletConnectSharp.Auth.csproj b/WalletConnectSharp.Auth/WalletConnectSharp.Auth.csproj index cec29da..4d253b9 100644 --- a/WalletConnectSharp.Auth/WalletConnectSharp.Auth.csproj +++ b/WalletConnectSharp.Auth/WalletConnectSharp.Auth.csproj @@ -27,4 +27,8 @@ + + + + diff --git a/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs b/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs index 98f517a..18bc3e5 100644 --- a/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs +++ b/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs @@ -179,10 +179,12 @@ public Task> Get(string topic, long id) IsInitialized(); var record = GetRecord(id); - if (topic != record.Topic) + + // TODO Log + /*if (topic != record.Topic) { throw WalletConnectException.FromType(ErrorType.MISMATCHED_TOPIC, $"{Name}: {id}"); - } + }*/ return Task.FromResult>(record); } diff --git a/WalletConnectSharp.Core/Controllers/Publisher.cs b/WalletConnectSharp.Core/Controllers/Publisher.cs index c9f0d4a..e2e1bf4 100644 --- a/WalletConnectSharp.Core/Controllers/Publisher.cs +++ b/WalletConnectSharp.Core/Controllers/Publisher.cs @@ -151,6 +151,7 @@ public async Task Publish(string topic, string message, PublishOptions opts = nu queue.Add(hash, @params); await RpcPublish(topic, message, @params.Options.TTL, @params.Options.Tag, @params.Options.Relay); OnPublish(hash); + this.Relayer.Events.Trigger(RelayerEvents.Publish, @params); } } } diff --git a/WalletConnectSharp.Core/Controllers/Store.cs b/WalletConnectSharp.Core/Controllers/Store.cs index 9469282..7e0b0c7 100644 --- a/WalletConnectSharp.Core/Controllers/Store.cs +++ b/WalletConnectSharp.Core/Controllers/Store.cs @@ -199,7 +199,9 @@ public Task Update(TKey key, TValue update) // If it exists (its not null), then set it if (@value != null) { - prop.SetValue(previousValue, null); + object test = previousValue; + prop.SetValue(test, @value, null); + previousValue = (TValue)test; } } diff --git a/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs b/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs index a37bf00..86cd1e9 100644 --- a/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs +++ b/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs @@ -11,7 +11,8 @@ namespace WalletConnectSharp.Core.Controllers public class TypedMessageHandler : ITypedMessageHandler { private bool _initialized = false; - + private Dictionary _decodeOptionsMap = new Dictionary(); + public EventDelegator Events { get; } public ICore Core { get; } @@ -60,7 +61,9 @@ async void RelayerMessageCallback(object sender, GenericEvent e) var topic = e.EventData.Topic; var message = e.EventData.Message; - var payload = await this.Core.Crypto.Decode(topic, message); + var options = DecodeOptionForTopic(topic); + + var payload = await this.Core.Crypto.Decode(topic, message, options); if (payload.IsRequest) { Events.Trigger($"request_{payload.Method}", e.EventData); @@ -95,8 +98,10 @@ async void RequestCallback(object sender, GenericEvent e) var topic = e.EventData.Topic; var message = e.EventData.Message; + + var options = DecodeOptionForTopic(topic); - var payload = await this.Core.Crypto.Decode>(topic, message); + var payload = await this.Core.Crypto.Decode>(topic, message, options); (await this.Core.History.JsonRpcHistoryOfType()).Set(topic, payload, null); @@ -109,8 +114,10 @@ async void ResponseCallback(object sender, GenericEvent e) var topic = e.EventData.Topic; var message = e.EventData.Message; + + var options = DecodeOptionForTopic(topic); - var payload = await this.Core.Crypto.Decode>(topic, message); + var payload = await this.Core.Crypto.Decode>(topic, message, options); await (await this.Core.History.JsonRpcHistoryOfType()).Resolve(payload); @@ -256,7 +263,19 @@ public PublishOptions RpcResponseOptionsForType() TTL = opts.TTL }; } - + + public void SetDecodeOptionsForTopic(DecodeOptions options, string topic) + { + _decodeOptionsMap.Add(topic, options); + } + + public DecodeOptions DecodeOptionForTopic(string topic) + { + if (_decodeOptionsMap.ContainsKey(topic)) + return _decodeOptionsMap[topic]; + return null; + } + /// /// Send a typed request message with the given request / response type pair T, TR to the given topic /// diff --git a/WalletConnectSharp.Core/Interfaces/ITypedMessageHandler.cs b/WalletConnectSharp.Core/Interfaces/ITypedMessageHandler.cs index 9301ef6..4d01ed5 100644 --- a/WalletConnectSharp.Core/Interfaces/ITypedMessageHandler.cs +++ b/WalletConnectSharp.Core/Interfaces/ITypedMessageHandler.cs @@ -75,6 +75,25 @@ void HandleMessageType(Func, Task> requestCallb /// If no is found in the type T PublishOptions RpcResponseOptionsForType(); + /// + /// Set the decode options that should be used whenever a message in the given + /// topic is received. By default, no decode options are used when a message is received. + /// Use this function to set decode options for Type1 messages inside a specific topic + /// + /// The decode options to use for all messages received in a specific topic + /// The topic to set the given decode options for + void SetDecodeOptionsForTopic(DecodeOptions options, string topic); + + /// + /// Get the decode options for the given topic, all messages received in the given topic + /// will be decoded using these decode options. If no decode options are set for the given + /// topic, then null is returned. + /// + /// The topic to get decode options for + /// The decode options set for the given topic. If no decode options + /// are set for the given topic, then null is returned. + DecodeOptions DecodeOptionForTopic(string topic); + /// /// Send a typed request message with the given request / response type pair T, TR to the given topic /// diff --git a/WalletConnectSharp.Core/Models/Relay/RelayerEvents.cs b/WalletConnectSharp.Core/Models/Relay/RelayerEvents.cs index 3375815..d6b9057 100644 --- a/WalletConnectSharp.Core/Models/Relay/RelayerEvents.cs +++ b/WalletConnectSharp.Core/Models/Relay/RelayerEvents.cs @@ -5,6 +5,11 @@ namespace WalletConnectSharp.Core.Models.Relay /// public static class RelayerEvents { + /// + /// The event id for the publish event + /// + public static readonly string Publish = "relayer_publish"; + /// /// The event id for the message event /// diff --git a/WalletConnectSharpV2.sln b/WalletConnectSharpV2.sln index 2a98ab7..3d8ef54 100644 --- a/WalletConnectSharpV2.sln +++ b/WalletConnectSharpV2.sln @@ -36,6 +36,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WalletConnectSharp.Tests.Co EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WalletConnectSharp.Auth", "WalletConnectSharp.Auth\WalletConnectSharp.Auth.csproj", "{5189138F-FAE5-4B2C-912F-CEB429C156AC}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WalletConnectSharp.Auth.Tests", "Tests\WalletConnectSharp.Auth.Tests\WalletConnectSharp.Auth.Tests.csproj", "{7B63C8BD-737F-403F-AF66-8A2E7ADBDBAB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -106,6 +108,10 @@ Global {5189138F-FAE5-4B2C-912F-CEB429C156AC}.Debug|Any CPU.Build.0 = Debug|Any CPU {5189138F-FAE5-4B2C-912F-CEB429C156AC}.Release|Any CPU.ActiveCfg = Release|Any CPU {5189138F-FAE5-4B2C-912F-CEB429C156AC}.Release|Any CPU.Build.0 = Release|Any CPU + {7B63C8BD-737F-403F-AF66-8A2E7ADBDBAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7B63C8BD-737F-403F-AF66-8A2E7ADBDBAB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7B63C8BD-737F-403F-AF66-8A2E7ADBDBAB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7B63C8BD-737F-403F-AF66-8A2E7ADBDBAB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {23897558-4177-425D-B2FA-75AB0B6FEDAE} = {3A55D6C7-0EF8-4EEA-90A5-89F5AFB97BA0} @@ -121,5 +127,6 @@ Global {3B89EF77-B544-4663-9E67-7E0CBF070BB3} = {3A55D6C7-0EF8-4EEA-90A5-89F5AFB97BA0} {5C78A5E4-24ED-499C-91BC-90D16A626EFA} = {3A55D6C7-0EF8-4EEA-90A5-89F5AFB97BA0} {C3CC2BC9-CD29-46B8-94DB-B104E427330B} = {3A55D6C7-0EF8-4EEA-90A5-89F5AFB97BA0} + {7B63C8BD-737F-403F-AF66-8A2E7ADBDBAB} = {3A55D6C7-0EF8-4EEA-90A5-89F5AFB97BA0} EndGlobalSection EndGlobal From a05390f5751ab71462e0882c459be64ac39e9829 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Mon, 5 Jun 2023 12:30:38 -0400 Subject: [PATCH 04/36] chore: bump version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 9962a60..642210f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 2.0.3 + 2.1.0 net6.0;netcoreapp3.1;netstandard2.1; From 4a46aacf1941e4351d55d626849c7020316cab84 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Tue, 6 Jun 2023 13:58:37 -0400 Subject: [PATCH 05/36] feat: complete web3wallet lib --- .../Models/RpcOptionsAttribute.cs | 1 + .../Models/RpcRequestOptionsAttribute.cs | 24 +++ .../Models/RpcResponseOptionsAttribute.cs | 24 +++ .../AuthClientFixture.cs | 8 +- .../AuthClientTest.cs | 1 - .../BiDirectional.cs | 3 +- .../SimpleExample.cs | 7 +- .../SignClientFixture.cs | 2 +- WalletConnectSharp.Auth/AuthMetadata.cs | 14 ++ WalletConnectSharp.Auth/Cacao.cs | 3 +- .../Controllers/AuthEngine.cs | 3 +- .../Interfaces/IAuthClient.cs | 3 +- WalletConnectSharp.Auth/Models/AuthOptions.cs | 2 +- WalletConnectSharp.Auth/Models/Metadata.cs | 24 --- WalletConnectSharp.Auth/Models/Requester.cs | 2 +- ...thClient.cs => WalletConnectAuthClient.cs} | 11 +- WalletConnectSharp.Auth/WcAuthRequest.cs | 3 +- WalletConnectSharp.Core/Controllers/Store.cs | 8 + WalletConnectSharp.Core/Interfaces/IStore.cs | 2 + .../{Models/Pairing => }/Metadata.cs | 2 +- .../Controllers/PendingRequests.cs | 13 ++ WalletConnectSharp.Sign/Engine.cs | 12 ++ .../Interfaces/IEngineAPI.cs | 5 + .../Interfaces/IEnginePrivate.cs | 4 + .../Interfaces/IPendingRequests.cs | 6 + .../Interfaces/ISignClient.cs | 3 + .../Internals/EngineHandler.cs | 7 + .../Internals/EngineTasks.cs | 22 +++ WalletConnectSharp.Sign/Models/Participant.cs | 1 + .../Models/PendingRequestStruct.cs | 24 +++ .../Models/SignClientOptions.cs | 1 + .../WalletConnectSignClient.cs | 16 +- .../Controllers/Web3WalletEngine.cs | 187 ++++++++++++++++++ .../Interfaces/IWeb3Wallet.cs | 15 ++ .../Interfaces/IWeb3WalletApi.cs | 48 +++++ .../Interfaces/IWeb3WalletEngine.cs | 20 ++ .../Models/BaseEventArgs.cs | 15 ++ .../Models/SessionRequestEventArgs.cs | 11 ++ .../WalletConnectSharp.Web3Wallet.csproj | 32 +++ WalletConnectSharp.Web3Wallet/Web3Wallet.cs | 149 ++++++++++++++ WalletConnectSharpV2.sln | 6 + 41 files changed, 691 insertions(+), 53 deletions(-) create mode 100644 WalletConnectSharp.Auth/AuthMetadata.cs delete mode 100644 WalletConnectSharp.Auth/Models/Metadata.cs rename WalletConnectSharp.Auth/{AuthClient.cs => WalletConnectAuthClient.cs} (93%) rename WalletConnectSharp.Core/{Models/Pairing => }/Metadata.cs (95%) create mode 100644 WalletConnectSharp.Sign/Controllers/PendingRequests.cs create mode 100644 WalletConnectSharp.Sign/Interfaces/IPendingRequests.cs create mode 100644 WalletConnectSharp.Sign/Models/PendingRequestStruct.cs create mode 100644 WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs create mode 100644 WalletConnectSharp.Web3Wallet/Interfaces/IWeb3Wallet.cs create mode 100644 WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletApi.cs create mode 100644 WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletEngine.cs create mode 100644 WalletConnectSharp.Web3Wallet/Models/BaseEventArgs.cs create mode 100644 WalletConnectSharp.Web3Wallet/Models/SessionRequestEventArgs.cs create mode 100644 WalletConnectSharp.Web3Wallet/WalletConnectSharp.Web3Wallet.csproj create mode 100644 WalletConnectSharp.Web3Wallet/Web3Wallet.cs diff --git a/Core Modules/WalletConnectSharp.Network/Models/RpcOptionsAttribute.cs b/Core Modules/WalletConnectSharp.Network/Models/RpcOptionsAttribute.cs index 39db21e..5530998 100644 --- a/Core Modules/WalletConnectSharp.Network/Models/RpcOptionsAttribute.cs +++ b/Core Modules/WalletConnectSharp.Network/Models/RpcOptionsAttribute.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; namespace WalletConnectSharp.Network.Models { diff --git a/Core Modules/WalletConnectSharp.Network/Models/RpcRequestOptionsAttribute.cs b/Core Modules/WalletConnectSharp.Network/Models/RpcRequestOptionsAttribute.cs index d6bd813..2906461 100644 --- a/Core Modules/WalletConnectSharp.Network/Models/RpcRequestOptionsAttribute.cs +++ b/Core Modules/WalletConnectSharp.Network/Models/RpcRequestOptionsAttribute.cs @@ -10,6 +10,30 @@ namespace WalletConnectSharp.Network.Models [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] public class RpcRequestOptionsAttribute : RpcOptionsAttribute { + /// + /// Returns the first RpcOptionsAttribute found on the given type T. + /// If no attribute is found, then null is returned + /// + /// The type to inspect for RpcOptionsAttribute + /// The first RpcOptionsAttribute found on the given type T + public static RpcRequestOptionsAttribute GetOptionsForType() + { + return GetOptionsForType(typeof(T)); + } + + /// + /// Returns the first RpcOptionsAttribute found on the given type t. + /// If no attribute is found, then null is returned + /// + /// The type to inspect for RpcOptionsAttribute + /// The first RpcOptionsAttribute found on the given type t + public static RpcRequestOptionsAttribute GetOptionsForType(Type t) + { + var attribute = t.GetCustomAttributes(typeof(RpcRequestOptionsAttribute), true).Cast().FirstOrDefault(); + + return attribute; + } + public RpcRequestOptionsAttribute(long ttl, int tag) : base(ttl, tag) { } diff --git a/Core Modules/WalletConnectSharp.Network/Models/RpcResponseOptionsAttribute.cs b/Core Modules/WalletConnectSharp.Network/Models/RpcResponseOptionsAttribute.cs index b5d0af8..03574d1 100644 --- a/Core Modules/WalletConnectSharp.Network/Models/RpcResponseOptionsAttribute.cs +++ b/Core Modules/WalletConnectSharp.Network/Models/RpcResponseOptionsAttribute.cs @@ -10,6 +10,30 @@ namespace WalletConnectSharp.Network.Models [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] public class RpcResponseOptionsAttribute : RpcOptionsAttribute { + /// + /// Returns the first RpcOptionsAttribute found on the given type T. + /// If no attribute is found, then null is returned + /// + /// The type to inspect for RpcOptionsAttribute + /// The first RpcOptionsAttribute found on the given type T + public static RpcResponseOptionsAttribute GetOptionsForType() + { + return GetOptionsForType(typeof(T)); + } + + /// + /// Returns the first RpcOptionsAttribute found on the given type t. + /// If no attribute is found, then null is returned + /// + /// The type to inspect for RpcOptionsAttribute + /// The first RpcOptionsAttribute found on the given type t + public static RpcResponseOptionsAttribute GetOptionsForType(Type t) + { + var attribute = t.GetCustomAttributes(typeof(RpcResponseOptionsAttribute), true).Cast().FirstOrDefault(); + + return attribute; + } + public RpcResponseOptionsAttribute(long ttl, int tag) : base(ttl, tag) { } diff --git a/Tests/WalletConnectSharp.Auth.Tests/AuthClientFixture.cs b/Tests/WalletConnectSharp.Auth.Tests/AuthClientFixture.cs index 9a88df4..4eacafa 100644 --- a/Tests/WalletConnectSharp.Auth.Tests/AuthClientFixture.cs +++ b/Tests/WalletConnectSharp.Auth.Tests/AuthClientFixture.cs @@ -17,7 +17,7 @@ protected override async void Init() { ProjectId = TestValues.TestProjectId, RelayUrl = TestValues.TestRelayUrl, - Metadata = new Metadata() + Metadata = new AuthMetadata() { Description = "An example dapp to showcase WalletConnectSharpv2", Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, @@ -32,7 +32,7 @@ protected override async void Init() { ProjectId = TestValues.TestProjectId, RelayUrl = TestValues.TestRelayUrl, - Metadata = new Metadata() + Metadata = new AuthMetadata() { Description = "An example wallet to showcase WalletConnectSharpv2", Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, @@ -43,7 +43,7 @@ protected override async void Init() Storage = new InMemoryStorage() }; - ClientA = await AuthClient.Init(OptionsA); - ClientB = await AuthClient.Init(OptionsB); + ClientA = await WalletConnectAuthClient.Init(OptionsA); + ClientB = await WalletConnectAuthClient.Init(OptionsB); } } diff --git a/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs b/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs index 0b19322..5925255 100644 --- a/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs +++ b/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs @@ -4,7 +4,6 @@ using WalletConnectSharp.Auth.Interfaces; using WalletConnectSharp.Auth.Internals; using WalletConnectSharp.Auth.Models; -using WalletConnectSharp.Auth.Models.Engine; using WalletConnectSharp.Core.Models.Pairing; using WalletConnectSharp.Core.Models.Publisher; using WalletConnectSharp.Core.Models.Relay; diff --git a/Tests/WalletConnectSharp.Examples/BiDirectional.cs b/Tests/WalletConnectSharp.Examples/BiDirectional.cs index 59eca0c..b7b51c7 100644 --- a/Tests/WalletConnectSharp.Examples/BiDirectional.cs +++ b/Tests/WalletConnectSharp.Examples/BiDirectional.cs @@ -1,5 +1,4 @@ -using WalletConnectSharp.Core.Models; -using WalletConnectSharp.Core.Models.Pairing; +using WalletConnectSharp.Core; using WalletConnectSharp.Sign; using WalletConnectSharp.Sign.Models; using WalletConnectSharp.Sign.Models.Engine; diff --git a/Tests/WalletConnectSharp.Examples/SimpleExample.cs b/Tests/WalletConnectSharp.Examples/SimpleExample.cs index d5b7135..5868d56 100644 --- a/Tests/WalletConnectSharp.Examples/SimpleExample.cs +++ b/Tests/WalletConnectSharp.Examples/SimpleExample.cs @@ -1,7 +1,4 @@ -using System; -using System.Threading.Tasks; -using WalletConnectSharp.Core.Models; -using WalletConnectSharp.Core.Models.Pairing; +using WalletConnectSharp.Core; using WalletConnectSharp.Sign; using WalletConnectSharp.Sign.Models; using WalletConnectSharp.Sign.Models.Engine; @@ -71,4 +68,4 @@ public async Task Execute(string[] args) } } } -} \ No newline at end of file +} diff --git a/Tests/WalletConnectSharp.Sign.Test/SignClientFixture.cs b/Tests/WalletConnectSharp.Sign.Test/SignClientFixture.cs index 78cf6c9..3533968 100644 --- a/Tests/WalletConnectSharp.Sign.Test/SignClientFixture.cs +++ b/Tests/WalletConnectSharp.Sign.Test/SignClientFixture.cs @@ -1,4 +1,4 @@ -using WalletConnectSharp.Core.Models.Pairing; +using WalletConnectSharp.Core; using WalletConnectSharp.Sign.Models; using WalletConnectSharp.Storage; using WalletConnectSharp.Tests.Common; diff --git a/WalletConnectSharp.Auth/AuthMetadata.cs b/WalletConnectSharp.Auth/AuthMetadata.cs new file mode 100644 index 0000000..7addc22 --- /dev/null +++ b/WalletConnectSharp.Auth/AuthMetadata.cs @@ -0,0 +1,14 @@ +using Newtonsoft.Json; +using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Core; + +namespace WalletConnectSharp.Auth; + +public class AuthMetadata : Metadata +{ + [JsonProperty("redirect")] + public RedirectData Redirect { get; set; } + + [JsonProperty("verifyUrl")] + public string VerifyUrl { get; set; } +} diff --git a/WalletConnectSharp.Auth/Cacao.cs b/WalletConnectSharp.Auth/Cacao.cs index 527d13a..b116823 100644 --- a/WalletConnectSharp.Auth/Cacao.cs +++ b/WalletConnectSharp.Auth/Cacao.cs @@ -1,9 +1,10 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using WalletConnectSharp.Auth.Models; using WalletConnectSharp.Common.Utils; using WalletConnectSharp.Network.Models; -namespace WalletConnectSharp.Auth.Models; +namespace WalletConnectSharp.Auth; [RpcResponseOptions(Clock.ONE_MINUTE, 3001)] public class Cacao : Message diff --git a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs index 1590cef..b853c27 100644 --- a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs +++ b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs @@ -2,7 +2,6 @@ using WalletConnectSharp.Auth.Interfaces; using WalletConnectSharp.Auth.Internals; using WalletConnectSharp.Auth.Models; -using WalletConnectSharp.Auth.Models.Engine; using WalletConnectSharp.Common.Model.Errors; using WalletConnectSharp.Common.Utils; using WalletConnectSharp.Core.Models.Relay; @@ -333,7 +332,7 @@ await this.Client.Requests.Set(payload.Id, }); } - private async Task GetVerifyContext(string hash, Metadata metadata) + private async Task GetVerifyContext(string hash, AuthMetadata metadata) { var context = new VerifiedContext() { diff --git a/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs b/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs index 4789a37..b73b192 100644 --- a/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs +++ b/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs @@ -1,5 +1,4 @@ using WalletConnectSharp.Auth.Models; -using WalletConnectSharp.Auth.Models.Engine; using WalletConnectSharp.Common; using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Events.Interfaces; @@ -16,7 +15,7 @@ public interface IAuthClient : IModule, IEvents event EventHandler AuthError; ICore Core { get; set; } - Metadata Metadata { get; set; } + AuthMetadata Metadata { get; set; } string ProjectId { get; set; } IStore AuthKeys { get; set; } IStore PairingTopics { get; set; } diff --git a/WalletConnectSharp.Auth/Models/AuthOptions.cs b/WalletConnectSharp.Auth/Models/AuthOptions.cs index a1e52f2..2ff2968 100644 --- a/WalletConnectSharp.Auth/Models/AuthOptions.cs +++ b/WalletConnectSharp.Auth/Models/AuthOptions.cs @@ -5,7 +5,7 @@ namespace WalletConnectSharp.Auth.Models; public class AuthOptions : CoreOptions { - public Metadata Metadata { get; set; } + public AuthMetadata Metadata { get; set; } public ICore Core { get; set; } } diff --git a/WalletConnectSharp.Auth/Models/Metadata.cs b/WalletConnectSharp.Auth/Models/Metadata.cs deleted file mode 100644 index bae7f84..0000000 --- a/WalletConnectSharp.Auth/Models/Metadata.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Newtonsoft.Json; - -namespace WalletConnectSharp.Auth.Models; - -public class Metadata -{ - [JsonProperty("name")] - public string Name { get; set; } - - [JsonProperty("description")] - public string Description { get; set; } - - [JsonProperty("url")] - public string Url { get; set; } - - [JsonProperty("icons")] - public string[] Icons { get; set; } - - [JsonProperty("redirect")] - public RedirectData Redirect { get; set; } - - [JsonProperty("verifyUrl")] - public string VerifyUrl { get; set; } -} diff --git a/WalletConnectSharp.Auth/Models/Requester.cs b/WalletConnectSharp.Auth/Models/Requester.cs index a1c6473..596a4a6 100644 --- a/WalletConnectSharp.Auth/Models/Requester.cs +++ b/WalletConnectSharp.Auth/Models/Requester.cs @@ -5,7 +5,7 @@ namespace WalletConnectSharp.Auth.Models; public class Requester { [JsonProperty("metadata")] - public Metadata Metadata { get; set; } + public AuthMetadata Metadata { get; set; } [JsonProperty("publicKey")] public string PublicKey { get; set; } diff --git a/WalletConnectSharp.Auth/AuthClient.cs b/WalletConnectSharp.Auth/WalletConnectAuthClient.cs similarity index 93% rename from WalletConnectSharp.Auth/AuthClient.cs rename to WalletConnectSharp.Auth/WalletConnectAuthClient.cs index 64d6c20..1d43643 100644 --- a/WalletConnectSharp.Auth/AuthClient.cs +++ b/WalletConnectSharp.Auth/WalletConnectAuthClient.cs @@ -1,14 +1,13 @@ using WalletConnectSharp.Auth.Controllers; using WalletConnectSharp.Auth.Interfaces; using WalletConnectSharp.Auth.Models; -using WalletConnectSharp.Auth.Models.Engine; using WalletConnectSharp.Core.Controllers; using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Events; namespace WalletConnectSharp.Auth; -public class AuthClient : IAuthClient +public class WalletConnectAuthClient : IAuthClient { public const string AUTH_CLIENT_PROTOCOL = AuthEngine.AUTH_CLIENT_PROTOCOL; public const int AUTH_CLIENT_VERSION = AuthEngine.AUTH_CLIENT_VERSION; @@ -53,7 +52,7 @@ public int Version public event EventHandler AuthResponded; public event EventHandler AuthError; public ICore Core { get; set; } - public Metadata Metadata { get; set; } + public AuthMetadata Metadata { get; set; } public string ProjectId { get; set; } public IStore AuthKeys { get; set; } public IStore PairingTopics { get; set; } @@ -75,9 +74,9 @@ public IDictionary PendingRequests } } - public static async Task Init(AuthOptions options) + public static async Task Init(AuthOptions options) { - var client = new AuthClient(options); + var client = new WalletConnectAuthClient(options); await client.Initialize(); return client; } @@ -91,7 +90,7 @@ private async Task Initialize() this.Engine.Init(); } - private AuthClient(AuthOptions options) + private WalletConnectAuthClient(AuthOptions options) { Options = options; Metadata = options.Metadata; diff --git a/WalletConnectSharp.Auth/WcAuthRequest.cs b/WalletConnectSharp.Auth/WcAuthRequest.cs index 81fe26b..dae4db9 100644 --- a/WalletConnectSharp.Auth/WcAuthRequest.cs +++ b/WalletConnectSharp.Auth/WcAuthRequest.cs @@ -1,8 +1,9 @@ using Newtonsoft.Json; +using WalletConnectSharp.Auth.Models; using WalletConnectSharp.Common.Utils; using WalletConnectSharp.Network.Models; -namespace WalletConnectSharp.Auth.Models.Engine; +namespace WalletConnectSharp.Auth; [RpcMethod("wc_authRequest")] [RpcRequestOptions(Clock.ONE_DAY, 3000)] diff --git a/WalletConnectSharp.Core/Controllers/Store.cs b/WalletConnectSharp.Core/Controllers/Store.cs index 7e0b0c7..08ea551 100644 --- a/WalletConnectSharp.Core/Controllers/Store.cs +++ b/WalletConnectSharp.Core/Controllers/Store.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; using WalletConnectSharp.Common; @@ -237,6 +238,13 @@ public Task Delete(TKey key, ErrorResponse reason) return Persist(); } + public IDictionary ToDictionary() + { + IsInitialized(); + + return new ReadOnlyDictionary(map); + } + protected virtual Task SetDataStore(TValue[] data) { return Core.Storage.SetItem(StorageKey, data); diff --git a/WalletConnectSharp.Core/Interfaces/IStore.cs b/WalletConnectSharp.Core/Interfaces/IStore.cs index 4fa4535..7db3106 100644 --- a/WalletConnectSharp.Core/Interfaces/IStore.cs +++ b/WalletConnectSharp.Core/Interfaces/IStore.cs @@ -65,5 +65,7 @@ public interface IStore : IModule where TValue : IKeyHolder /// The reason this key was deleted using an ErrorResponse /// public Task Delete(TKey key, ErrorResponse reason); + + public IDictionary ToDictionary(); } } diff --git a/WalletConnectSharp.Core/Models/Pairing/Metadata.cs b/WalletConnectSharp.Core/Metadata.cs similarity index 95% rename from WalletConnectSharp.Core/Models/Pairing/Metadata.cs rename to WalletConnectSharp.Core/Metadata.cs index 64f8198..9d59403 100644 --- a/WalletConnectSharp.Core/Models/Pairing/Metadata.cs +++ b/WalletConnectSharp.Core/Metadata.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace WalletConnectSharp.Core.Models.Pairing +namespace WalletConnectSharp.Core { /// /// A class that holds Metadata for either peer in a given Session. Includes things such diff --git a/WalletConnectSharp.Sign/Controllers/PendingRequests.cs b/WalletConnectSharp.Sign/Controllers/PendingRequests.cs new file mode 100644 index 0000000..3831601 --- /dev/null +++ b/WalletConnectSharp.Sign/Controllers/PendingRequests.cs @@ -0,0 +1,13 @@ +using WalletConnectSharp.Core.Controllers; +using WalletConnectSharp.Core.Interfaces; +using WalletConnectSharp.Sign.Interfaces; +using WalletConnectSharp.Sign.Models; + +namespace WalletConnectSharp.Sign.Controllers; + +public class PendingRequests : Store, IPendingRequests +{ + public PendingRequests(ICore core) : base(core, $"request", WalletConnectSignClient.StoragePrefix) + { + } +} diff --git a/WalletConnectSharp.Sign/Engine.cs b/WalletConnectSharp.Sign/Engine.cs index 7c39cbe..617b856 100644 --- a/WalletConnectSharp.Sign/Engine.cs +++ b/WalletConnectSharp.Sign/Engine.cs @@ -178,6 +178,18 @@ public UriParameters ParseUri(string uri) return result; } + /// + /// Get all pending session requests + /// + public PendingRequestStruct[] PendingSessionRequests + { + get + { + this.IsInitialized(); + return this.Client.PendingRequests.Values; + } + } + /// /// Connect (a dApp) with the given ConnectOptions. At a minimum, you must specified a RequiredNamespace. /// diff --git a/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs b/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs index 557d2ee..3e66496 100644 --- a/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs +++ b/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs @@ -12,6 +12,11 @@ namespace WalletConnectSharp.Sign.Interfaces /// public interface IEngineAPI { + /// + /// Get all pending session requests as an array + /// + PendingRequestStruct[] PendingSessionRequests { get; } + /// /// Connect (a dApp) with the given ConnectOptions. At a minimum, you must specified a RequiredNamespace. /// diff --git a/WalletConnectSharp.Sign/Interfaces/IEnginePrivate.cs b/WalletConnectSharp.Sign/Interfaces/IEnginePrivate.cs index 4477f39..e97ba31 100644 --- a/WalletConnectSharp.Sign/Interfaces/IEnginePrivate.cs +++ b/WalletConnectSharp.Sign/Interfaces/IEnginePrivate.cs @@ -68,5 +68,9 @@ internal interface IEnginePrivate internal Task IsValidEmit(string topic, EventData request, string chainId); internal Task IsValidDisconnect(string topic, ErrorResponse reason); + + internal Task DeletePendingSessionRequest(long id, ErrorResponse reason, bool expirerHasDeleted = false); + + internal Task SetPendingSessionRequest(PendingRequestStruct pendingRequest); } } diff --git a/WalletConnectSharp.Sign/Interfaces/IPendingRequests.cs b/WalletConnectSharp.Sign/Interfaces/IPendingRequests.cs new file mode 100644 index 0000000..a526e45 --- /dev/null +++ b/WalletConnectSharp.Sign/Interfaces/IPendingRequests.cs @@ -0,0 +1,6 @@ +using WalletConnectSharp.Core.Interfaces; +using WalletConnectSharp.Sign.Models; + +namespace WalletConnectSharp.Sign.Interfaces; + +public interface IPendingRequests : IStore { } diff --git a/WalletConnectSharp.Sign/Interfaces/ISignClient.cs b/WalletConnectSharp.Sign/Interfaces/ISignClient.cs index edc8b8a..4d64cc5 100644 --- a/WalletConnectSharp.Sign/Interfaces/ISignClient.cs +++ b/WalletConnectSharp.Sign/Interfaces/ISignClient.cs @@ -1,4 +1,5 @@ using WalletConnectSharp.Common; +using WalletConnectSharp.Core; using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Core.Models; using WalletConnectSharp.Core.Models.Pairing; @@ -37,6 +38,8 @@ public interface ISignClient : IModule, IEvents, IEngineAPI /// The module this Sign Client is using to store Proposal data /// IProposal Proposal { get; } + + IPendingRequests PendingRequests { get; } /// /// The options this Sign Client was initialized with diff --git a/WalletConnectSharp.Sign/Internals/EngineHandler.cs b/WalletConnectSharp.Sign/Internals/EngineHandler.cs index 572442c..8ab0027 100644 --- a/WalletConnectSharp.Sign/Internals/EngineHandler.cs +++ b/WalletConnectSharp.Sign/Internals/EngineHandler.cs @@ -18,6 +18,13 @@ async void ExpiredCallback(object sender, GenericEvent e) { var target = new ExpirerTarget(e.EventData.Target); + if (target.Id != null && this.Client.PendingRequests.Keys.Contains((long)target.Id)) + { + await PrivateThis.DeletePendingSessionRequest((long)target.Id, + ErrorResponse.FromErrorType(ErrorType.EXPIRED), true); + return; + } + if (!string.IsNullOrWhiteSpace(target.Topic)) { var topic = target.Topic; diff --git a/WalletConnectSharp.Sign/Internals/EngineTasks.cs b/WalletConnectSharp.Sign/Internals/EngineTasks.cs index bd10003..8511343 100644 --- a/WalletConnectSharp.Sign/Internals/EngineTasks.cs +++ b/WalletConnectSharp.Sign/Internals/EngineTasks.cs @@ -9,11 +9,33 @@ using WalletConnectSharp.Sign.Interfaces; using WalletConnectSharp.Sign.Models; using WalletConnectSharp.Sign.Models.Engine; +using WalletConnectSharp.Sign.Models.Engine.Methods; namespace WalletConnectSharp.Sign { public partial class Engine { + async Task IEnginePrivate.DeletePendingSessionRequest(long id, ErrorResponse reason, bool expirerHasDeleted = false) + { + await Task.WhenAll( + this.Client.PendingRequests.Delete(id, reason), + expirerHasDeleted ? Task.CompletedTask : this.Client.Core.Expirer.Delete(id) + ); + } + + async Task IEnginePrivate.SetPendingSessionRequest(PendingRequestStruct pendingRequest) + { + var options = RpcRequestOptionsAttribute.GetOptionsForType>(); + var expiry = options.TTL; + + await this.Client.PendingRequests.Set(pendingRequest.Id, pendingRequest); + + if (expiry != 0) + { + this.Client.Core.Expirer.Set(pendingRequest.Id, Clock.CalculateExpiry(expiry)); + } + } + async Task IEnginePrivate.DeleteSession(string topic) { var session = this.Client.Session.Get(topic); diff --git a/WalletConnectSharp.Sign/Models/Participant.cs b/WalletConnectSharp.Sign/Models/Participant.cs index 95a7f63..1073121 100644 --- a/WalletConnectSharp.Sign/Models/Participant.cs +++ b/WalletConnectSharp.Sign/Models/Participant.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using WalletConnectSharp.Core; using WalletConnectSharp.Core.Models; using WalletConnectSharp.Core.Models.Pairing; diff --git a/WalletConnectSharp.Sign/Models/PendingRequestStruct.cs b/WalletConnectSharp.Sign/Models/PendingRequestStruct.cs new file mode 100644 index 0000000..a71bbe1 --- /dev/null +++ b/WalletConnectSharp.Sign/Models/PendingRequestStruct.cs @@ -0,0 +1,24 @@ +using WalletConnectSharp.Core.Interfaces; +using WalletConnectSharp.Sign.Models.Engine.Methods; + +namespace WalletConnectSharp.Sign.Models; + +public struct PendingRequestStruct : IKeyHolder +{ + public long Id { get; set; } + + public string Topic { get; set; } + + public long Key + { + get + { + return Id; + } + } + + // Specify object here, so we can store any type + // We don't care about type-safety for these pending + // requests + public SessionRequest Parameters { get; set; } +} diff --git a/WalletConnectSharp.Sign/Models/SignClientOptions.cs b/WalletConnectSharp.Sign/Models/SignClientOptions.cs index d0e8924..e4ea84c 100644 --- a/WalletConnectSharp.Sign/Models/SignClientOptions.cs +++ b/WalletConnectSharp.Sign/Models/SignClientOptions.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using WalletConnectSharp.Core; using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Core.Models; using WalletConnectSharp.Core.Models.Pairing; diff --git a/WalletConnectSharp.Sign/WalletConnectSignClient.cs b/WalletConnectSharp.Sign/WalletConnectSignClient.cs index 75cf295..e531c2f 100644 --- a/WalletConnectSharp.Sign/WalletConnectSignClient.cs +++ b/WalletConnectSharp.Sign/WalletConnectSignClient.cs @@ -1,4 +1,5 @@ using WalletConnectSharp.Common.Model.Errors; +using WalletConnectSharp.Core; using WalletConnectSharp.Core.Controllers; using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Core.Models; @@ -90,6 +91,8 @@ public class WalletConnectSignClient : ISignClient /// public IProposal Proposal { get; } + public IPendingRequests PendingRequests { get; } + /// /// The this Sign Client was initialized with. /// @@ -116,6 +119,15 @@ public int Version return VERSION; } } + + + public PendingRequestStruct[] PendingSessionRequests + { + get + { + return Engine.PendingSessionRequests; + } + } /// /// Create a new instance with the given @@ -171,7 +183,8 @@ private WalletConnectSignClient(SignClientOptions options) Core = new Core.Core(options); Events = new EventDelegator(this); - + + PendingRequests = new PendingRequests(Core); PairingStore = new PairingStore(Core); Session = new Session(Core); Proposal = new Proposal(Core); @@ -382,6 +395,7 @@ public SessionStruct[] Find(RequiredNamespaces requiredNamespaces) private async Task Initialize() { await this.Core.Start(); + await PendingRequests.Init(); await PairingStore.Init(); await Session.Init(); await Proposal.Init(); diff --git a/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs b/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs new file mode 100644 index 0000000..06eb6de --- /dev/null +++ b/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs @@ -0,0 +1,187 @@ +using WalletConnectSharp.Auth; +using WalletConnectSharp.Auth.Interfaces; +using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Common.Model.Errors; +using WalletConnectSharp.Events; +using WalletConnectSharp.Events.Interfaces; +using WalletConnectSharp.Events.Model; +using WalletConnectSharp.Network.Models; +using WalletConnectSharp.Sign; +using WalletConnectSharp.Sign.Interfaces; +using WalletConnectSharp.Sign.Models; +using WalletConnectSharp.Sign.Models.Engine; +using WalletConnectSharp.Sign.Models.Engine.Events; +using WalletConnectSharp.Web3Wallet.Interfaces; +using ErrorResponse = WalletConnectSharp.Network.Models.ErrorResponse; + +namespace WalletConnectSharp.Web3Wallet.Controllers; + +public class Web3WalletEngine : IWeb3WalletEngine +{ + private bool _initialized; + + public IDictionary ActiveSessions + { + get + { + return this.SignClient.Session.ToDictionary(); + } + } + + public IDictionary PendingSessionProposals + { + get + { + return this.SignClient.Proposal.ToDictionary(); + } + } + + public PendingRequestStruct[] PendingSessionRequests + { + get + { + return this.SignClient.PendingSessionRequests; + } + } + + public IDictionary PendingAuthRequests + { + get + { + return this.AuthClient.PendingRequests; + } + } + + public ISignClient SignClient { get; private set; } + public IAuthClient AuthClient { get; private set; } + public IWeb3Wallet Client { get; } + + public Web3WalletEngine(IWeb3Wallet client) + { + Client = client; + } + + public async Task Init() + { + this.SignClient = await WalletConnectSignClient.Init(new SignClientOptions() + { + Core = this.Client.Core, Metadata = this.Client.Metadata + }); + + this.AuthClient = await WalletConnectAuthClient.Init(new AuthOptions() + { + Core = this.Client.Core, ProjectId = this.Client.Core.ProjectId, Metadata = this.Client.Metadata + }); + + InitializeEventListeners(); + + _initialized = true; + } + + public async Task Pair(string uri, bool activatePairing = false) + { + IsInitialized(); + await this.Client.Core.Pairing.Pair(uri, activatePairing); + } + + public async Task ApproveSession(long id, Namespaces namespaces, string relayProtocol = null) + { + var data = await this.SignClient.Approve(new ApproveParams() + { + Id = id, Namespaces = namespaces, RelayProtocol = relayProtocol + }); + + await data.Acknowledged(); + + return this.SignClient.Session.Get(data.Topic); + } + + public Task ApproveSession(ProposalStruct proposal, params string[] approvedAddresses) + { + var param = proposal.ApproveProposal(approvedAddresses); + return ApproveSession(param.Id, param.Namespaces, param.RelayProtocol); + } + + public Task RejectSession(long id, ErrorResponse reason) + { + return this.SignClient.Reject(new RejectParams() { Id = id, Reason = reason }); + } + + public Task RejectSession(ProposalStruct proposal, ErrorResponse reason) + { + var parm = proposal.RejectProposal(reason); + return RejectSession(parm.Id, parm.Reason); + } + + public Task RejectSession(ProposalStruct proposal, string reason) + { + var parm = proposal.RejectProposal(reason); + return RejectSession(parm.Id, parm.Reason); + } + + public async Task UpdateSession(string topic, Namespaces namespaces) + { + await (await this.SignClient.Update(topic, namespaces)).Acknowledged(); + } + + public async Task ExtendSession(string topic) + { + await (await this.SignClient.Extend(topic)).Acknowledged(); + } + + public async Task RespondSessionRequest(string topic, JsonRpcResponse response) + { + await this.SignClient.Respond(topic, response); + } + + public async Task EmitSessionEvent(string topic, EventData eventData, string chainId) + { + await this.SignClient.Emit(topic, eventData, chainId); + } + + public async Task DisconnectSession(string topic, ErrorResponse reason) + { + await this.SignClient.Disconnect(topic, reason); + } + + public async Task RespondAuthRequest(ResultResponse results, string iss) + { + await this.AuthClient.Respond(results, iss); + } + + public async Task RespondAuthRequest(AuthErrorResponse error, string iss) + { + await this.AuthClient.Respond(error, iss); + } + + public string FormatMessage(Cacao.CacaoRequestPayload payload, string iss) + { + return this.AuthClient.FormatMessage(payload, iss); + } + + private void PropagateEventToClient(string eventName, IEvents source) + { + EventHandler> eventHandler = (sender, @event) => + { + this.Client.Events.Trigger(eventName, @event); + }; + + source.On(eventName, eventHandler); + } + + private void InitializeEventListeners() + { + PropagateEventToClient("session_proposal", SignClient); + PropagateEventToClient("session_request", SignClient); + PropagateEventToClient("session_delete", SignClient); + PropagateEventToClient("auth_request", AuthClient); + } + + private void IsInitialized() + { + if (!_initialized) + { + throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, "Web3WalletEngine"); + } + } +} diff --git a/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3Wallet.cs b/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3Wallet.cs new file mode 100644 index 0000000..d3ebd21 --- /dev/null +++ b/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3Wallet.cs @@ -0,0 +1,15 @@ +using WalletConnectSharp.Auth; +using WalletConnectSharp.Common; +using WalletConnectSharp.Core.Interfaces; +using WalletConnectSharp.Events.Interfaces; + +namespace WalletConnectSharp.Web3Wallet.Interfaces; + +public interface IWeb3Wallet : IModule, IEvents, IWeb3WalletApi +{ + IWeb3WalletEngine Engine { get; } + + ICore Core { get; } + + AuthMetadata Metadata { get; } +} diff --git a/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletApi.cs b/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletApi.cs new file mode 100644 index 0000000..67251c9 --- /dev/null +++ b/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletApi.cs @@ -0,0 +1,48 @@ +using WalletConnectSharp.Auth; +using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Network.Models; +using WalletConnectSharp.Sign.Models; +using WalletConnectSharp.Sign.Models.Engine.Events; +using ErrorResponse = WalletConnectSharp.Network.Models.ErrorResponse; + +namespace WalletConnectSharp.Web3Wallet.Interfaces; + +public interface IWeb3WalletApi +{ + IDictionary ActiveSessions { get; } + + IDictionary PendingSessionProposals { get; } + + PendingRequestStruct[] PendingSessionRequests { get; } + + IDictionary PendingAuthRequests { get; } + + Task Pair(string uri, bool activatePairing = false); + + Task ApproveSession(long id, Namespaces namespaces, string relayProtocol = null); + + Task ApproveSession(ProposalStruct proposal, params string[] approvedAddresses); + + Task RejectSession(long id, ErrorResponse reason); + + Task RejectSession(ProposalStruct proposal, ErrorResponse reason); + + + Task RejectSession(ProposalStruct proposal, string reason); + + Task UpdateSession(string topic, Namespaces namespaces); + + Task ExtendSession(string topic); + + Task RespondSessionRequest(string topic, JsonRpcResponse response); + + Task EmitSessionEvent(string topic, EventData eventData, string chainId); + + Task DisconnectSession(string topic, ErrorResponse reason); + + Task RespondAuthRequest(ResultResponse results, string iss); + + Task RespondAuthRequest(AuthErrorResponse error, string iss); + + string FormatMessage(Cacao.CacaoRequestPayload payload, string iss); +} diff --git a/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletEngine.cs b/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletEngine.cs new file mode 100644 index 0000000..21a4c21 --- /dev/null +++ b/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletEngine.cs @@ -0,0 +1,20 @@ +using WalletConnectSharp.Auth; +using WalletConnectSharp.Auth.Interfaces; +using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Network.Models; +using WalletConnectSharp.Sign.Interfaces; +using WalletConnectSharp.Sign.Models; +using ErrorResponse = WalletConnectSharp.Network.Models.ErrorResponse; + +namespace WalletConnectSharp.Web3Wallet.Interfaces; + +public interface IWeb3WalletEngine : IWeb3WalletApi +{ + ISignClient SignClient { get; } + + IAuthClient AuthClient { get; } + + IWeb3Wallet Client { get; } + + Task Init(); +} diff --git a/WalletConnectSharp.Web3Wallet/Models/BaseEventArgs.cs b/WalletConnectSharp.Web3Wallet/Models/BaseEventArgs.cs new file mode 100644 index 0000000..34eeb23 --- /dev/null +++ b/WalletConnectSharp.Web3Wallet/Models/BaseEventArgs.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace WalletConnectSharp.Web3Wallet.Models; + +public class BaseEventArgs : EventArgs +{ + [JsonProperty("id")] + public long Id { get; set; } + + [JsonProperty("topic")] + public string Topic { get; set; } + + [JsonProperty("params")] + public T Parameters { get; set; } +} diff --git a/WalletConnectSharp.Web3Wallet/Models/SessionRequestEventArgs.cs b/WalletConnectSharp.Web3Wallet/Models/SessionRequestEventArgs.cs new file mode 100644 index 0000000..4866eda --- /dev/null +++ b/WalletConnectSharp.Web3Wallet/Models/SessionRequestEventArgs.cs @@ -0,0 +1,11 @@ +using Newtonsoft.Json; +using WalletConnectSharp.Core.Models.Verify; +using WalletConnectSharp.Sign.Models.Engine.Methods; + +namespace WalletConnectSharp.Web3Wallet.Models; + +public class SessionRequestEventArgs : BaseEventArgs> +{ + [JsonProperty("verifyContext")] + public VerifiedContext VerifyContext { get; set; } +} diff --git a/WalletConnectSharp.Web3Wallet/WalletConnectSharp.Web3Wallet.csproj b/WalletConnectSharp.Web3Wallet/WalletConnectSharp.Web3Wallet.csproj new file mode 100644 index 0000000..df917a0 --- /dev/null +++ b/WalletConnectSharp.Web3Wallet/WalletConnectSharp.Web3Wallet.csproj @@ -0,0 +1,32 @@ + + + + $(DefaultTargetFrameworks) + $(DefaultVersion) + $(DefaultVersion) + $(DefaultVersion) + WalletConnect.Web3Wallet + WalletConnectSharp.Web3Wallet + pedrouid, gigajuwels + A port of the TypeScript SDK to C#. A complete implementation of the WalletConnect v2 protocol that can be used to connect to external wallets or connect a wallet to an external Dapp + Copyright (c) WalletConnect 2023 + https://walletconnect.org/ + icon.png + https://github.com/WalletConnect/WalletConnectSharp + git + web3wallet walletconnect sign wallet web3 ether ethereum blockchain evm + true + Apache-2.0 + + + + + + + + + + + + + diff --git a/WalletConnectSharp.Web3Wallet/Web3Wallet.cs b/WalletConnectSharp.Web3Wallet/Web3Wallet.cs new file mode 100644 index 0000000..40a16b4 --- /dev/null +++ b/WalletConnectSharp.Web3Wallet/Web3Wallet.cs @@ -0,0 +1,149 @@ +using WalletConnectSharp.Auth; +using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Core.Interfaces; +using WalletConnectSharp.Events; +using WalletConnectSharp.Network.Models; +using WalletConnectSharp.Sign.Models; +using WalletConnectSharp.Sign.Models.Engine.Events; +using WalletConnectSharp.Web3Wallet.Controllers; +using WalletConnectSharp.Web3Wallet.Interfaces; +using ErrorResponse = WalletConnectSharp.Network.Models.ErrorResponse; + +namespace WalletConnectSharp.Web3Wallet; + +public class Web3Wallet : IWeb3Wallet +{ + public string Name { get; } + public string Context { get; } + public EventDelegator Events { get; } + + public IDictionary ActiveSessions + { + get + { + return this.Engine.ActiveSessions; + } + } + + public IDictionary PendingSessionProposals + { + get + { + return this.Engine.PendingSessionProposals; + } + } + + public PendingRequestStruct[] PendingSessionRequests + { + get + { + return this.Engine.PendingSessionRequests; + } + } + + public IDictionary PendingAuthRequests + { + get + { + return this.Engine.PendingAuthRequests; + } + } + + public IWeb3WalletEngine Engine { get; } + public ICore Core { get; } + public AuthMetadata Metadata { get; } + + public static async Task Init(ICore core, AuthMetadata metadata, string name = null) + { + var wallet = new Web3Wallet(core, metadata, name); + await wallet.Initialize(); + + return wallet; + } + + private Web3Wallet(ICore core, AuthMetadata metadata, string name = null) + { + this.Metadata = metadata; + this.Name = string.IsNullOrWhiteSpace(name) ? "Web3Wallet" : name; + this.Context = $"{Name}-context"; + this.Core = core; + + this.Events = new EventDelegator(this); + this.Engine = new Web3WalletEngine(this); + } + + public Task Pair(string uri, bool activatePairing = false) + { + return this.Engine.Pair(uri, activatePairing); + } + + public Task ApproveSession(long id, Namespaces namespaces, string relayProtocol = null) + { + return this.Engine.ApproveSession(id, namespaces, relayProtocol); + } + + public Task ApproveSession(ProposalStruct proposal, params string[] approvedAddresses) + { + return this.Engine.ApproveSession(proposal, approvedAddresses); + } + + public Task RejectSession(long id, ErrorResponse reason) + { + return this.Engine.RejectSession(id, reason); + } + + public Task RejectSession(ProposalStruct proposal, ErrorResponse reason) + { + return this.Engine.RejectSession(proposal, reason); + } + + public Task RejectSession(ProposalStruct proposal, string reason) + { + return this.Engine.RejectSession(proposal, reason); + } + + public Task UpdateSession(string topic, Namespaces namespaces) + { + return this.Engine.UpdateSession(topic, namespaces); + } + + public Task ExtendSession(string topic) + { + return this.Engine.ExtendSession(topic); + } + + public Task RespondSessionRequest(string topic, JsonRpcResponse response) + { + return this.Engine.RespondSessionRequest(topic, response); + } + + public Task EmitSessionEvent(string topic, EventData eventData, string chainId) + { + return this.Engine.EmitSessionEvent(topic, eventData, topic); + } + + public Task DisconnectSession(string topic, ErrorResponse reason) + { + return this.Engine.DisconnectSession(topic, reason); + } + + public Task RespondAuthRequest(ResultResponse results, string iss) + { + return this.Engine.RespondAuthRequest(results, iss); + } + + public Task RespondAuthRequest(AuthErrorResponse error, string iss) + { + return this.Engine.RespondAuthRequest(error, iss); + } + + public string FormatMessage(Cacao.CacaoRequestPayload payload, string iss) + { + return this.Engine.FormatMessage(payload, iss); + } + + private Task Initialize() + { + return this.Engine.Init(); + } +} diff --git a/WalletConnectSharpV2.sln b/WalletConnectSharpV2.sln index 3d8ef54..2a0ff3c 100644 --- a/WalletConnectSharpV2.sln +++ b/WalletConnectSharpV2.sln @@ -38,6 +38,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WalletConnectSharp.Auth", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WalletConnectSharp.Auth.Tests", "Tests\WalletConnectSharp.Auth.Tests\WalletConnectSharp.Auth.Tests.csproj", "{7B63C8BD-737F-403F-AF66-8A2E7ADBDBAB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WalletConnectSharp.Web3Wallet", "WalletConnectSharp.Web3Wallet\WalletConnectSharp.Web3Wallet.csproj", "{53622C78-1162-46A2-9FFE-8E90B018A144}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -112,6 +114,10 @@ Global {7B63C8BD-737F-403F-AF66-8A2E7ADBDBAB}.Debug|Any CPU.Build.0 = Debug|Any CPU {7B63C8BD-737F-403F-AF66-8A2E7ADBDBAB}.Release|Any CPU.ActiveCfg = Release|Any CPU {7B63C8BD-737F-403F-AF66-8A2E7ADBDBAB}.Release|Any CPU.Build.0 = Release|Any CPU + {53622C78-1162-46A2-9FFE-8E90B018A144}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {53622C78-1162-46A2-9FFE-8E90B018A144}.Debug|Any CPU.Build.0 = Debug|Any CPU + {53622C78-1162-46A2-9FFE-8E90B018A144}.Release|Any CPU.ActiveCfg = Release|Any CPU + {53622C78-1162-46A2-9FFE-8E90B018A144}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {23897558-4177-425D-B2FA-75AB0B6FEDAE} = {3A55D6C7-0EF8-4EEA-90A5-89F5AFB97BA0} From 605692c79e87bc99c3692630df972f619c48e0da Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Sun, 11 Jun 2023 16:43:04 -0400 Subject: [PATCH 06/36] feat: web3wallet --- .../WebsocketConnection.cs | 2 +- .../Interfaces/IJsonRpcError.cs | 2 +- .../Models/{ErrorResponse.cs => Error.cs} | 10 +- .../Models/JsonRpcError.cs | 4 +- .../Models/JsonRpcResponse.cs | 4 +- .../AuthClientTest.cs | 2 +- .../AuthClientTest.cs | 524 ++++++++++++++++++ ...WalletConnectSharp.Web3Wallet.Tests.csproj | 29 + .../Web3WalletFixture.cs | 63 +++ .../Controllers/AuthEngine.cs | 3 +- .../Models/AuthErrorResponse.cs | 4 +- .../Models/ErrorResponse.cs | 5 +- .../Controllers/Pairing.cs | 10 +- WalletConnectSharp.Core/Controllers/Store.cs | 2 +- .../Controllers/Subscriber.cs | 6 +- .../Controllers/TypedMessageHandler.cs | 2 +- WalletConnectSharp.Core/Interfaces/IStore.cs | 2 +- .../Interfaces/ITypedMessageHandler.cs | 2 +- .../Models/MessageHandler/RequestEventArgs.cs | 2 +- .../Models/Pairing/Methods/PairingDelete.cs | 2 +- .../Models/Subscriber/DeletedSubscription.cs | 2 +- WalletConnectSharp.Sign/Engine.cs | 12 +- .../Interfaces/IEngineAPI.cs | 6 +- .../Interfaces/IEnginePrivate.cs | 4 +- .../Internals/EngineHandler.cs | 22 +- .../Internals/EngineTasks.cs | 6 +- .../Internals/EngineValidation.cs | 30 +- .../Models/Engine/Methods/SessionDelete.cs | 2 +- .../Models/Engine/RejectParams.cs | 6 +- .../Models/ProposalStruct.cs | 6 +- .../WalletConnectSignClient.cs | 8 +- .../Controllers/Web3WalletEngine.cs | 7 +- .../Interfaces/IWeb3WalletApi.cs | 7 +- .../Interfaces/IWeb3WalletEngine.cs | 1 - .../{Web3Wallet.cs => Web3WalletClient.cs} | 15 +- WalletConnectSharpV2.sln | 7 + 36 files changed, 720 insertions(+), 101 deletions(-) rename Core Modules/WalletConnectSharp.Network/Models/{ErrorResponse.cs => Error.cs} (87%) create mode 100644 Tests/WalletConnectSharp.Web3Wallet.Tests/AuthClientTest.cs create mode 100644 Tests/WalletConnectSharp.Web3Wallet.Tests/WalletConnectSharp.Web3Wallet.Tests.csproj create mode 100644 Tests/WalletConnectSharp.Web3Wallet.Tests/Web3WalletFixture.cs rename WalletConnectSharp.Web3Wallet/{Web3Wallet.cs => Web3WalletClient.cs} (86%) diff --git a/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnection.cs b/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnection.cs index 643c33d..7cf134c 100644 --- a/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnection.cs +++ b/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnection.cs @@ -322,7 +322,7 @@ private void OnError(IJsonRpcPayload ogPayload, Exception e) ? new IOException("Unavailable WS RPC url at " + _url) : e; var message = exception.Message; - var payload = new JsonRpcResponse(ogPayload.Id, new ErrorResponse() + var payload = new JsonRpcResponse(ogPayload.Id, new Error() { Code = exception.HResult, Data = null, diff --git a/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcError.cs b/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcError.cs index 02c99e3..9791ada 100644 --- a/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcError.cs +++ b/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcError.cs @@ -12,6 +12,6 @@ public interface IJsonRpcError : IJsonRpcPayload /// The error for this JSON RPC response or null if no error is present /// [JsonProperty("error")] - ErrorResponse Error { get; } + Error Error { get; } } } \ No newline at end of file diff --git a/Core Modules/WalletConnectSharp.Network/Models/ErrorResponse.cs b/Core Modules/WalletConnectSharp.Network/Models/Error.cs similarity index 87% rename from Core Modules/WalletConnectSharp.Network/Models/ErrorResponse.cs rename to Core Modules/WalletConnectSharp.Network/Models/Error.cs index cf9e0fe..c091df7 100644 --- a/Core Modules/WalletConnectSharp.Network/Models/ErrorResponse.cs +++ b/Core Modules/WalletConnectSharp.Network/Models/Error.cs @@ -9,7 +9,7 @@ namespace WalletConnectSharp.Network.Models /// /// Indicates an error /// - public class ErrorResponse + public class Error { /// /// The error code of this error @@ -36,11 +36,11 @@ public class ErrorResponse /// Extra parameters for the error message /// Extra data that is stored in the Data field of the newly created ErrorResponse /// A new ErrorResponse - public static ErrorResponse FromErrorType(ErrorType type, object @params = null, string extraData = null) + public static Error FromErrorType(ErrorType type, object @params = null, string extraData = null) { string message = SdkErrors.MessageFromType(type, @params); - return new ErrorResponse() + return new Error() { Code = (long) type, Message = message, @@ -53,9 +53,9 @@ public static ErrorResponse FromErrorType(ErrorType type, object @params = null, /// /// The exception to grab error values from /// A new ErrorResponse object using values from the given exception - public static ErrorResponse FromException(WalletConnectException walletConnectException) + public static Error FromException(WalletConnectException walletConnectException) { - return new ErrorResponse() + return new Error() { Code = walletConnectException.Code, Message = walletConnectException.Message, diff --git a/Core Modules/WalletConnectSharp.Network/Models/JsonRpcError.cs b/Core Modules/WalletConnectSharp.Network/Models/JsonRpcError.cs index 9503580..4d31fd5 100644 --- a/Core Modules/WalletConnectSharp.Network/Models/JsonRpcError.cs +++ b/Core Modules/WalletConnectSharp.Network/Models/JsonRpcError.cs @@ -29,7 +29,7 @@ public string JsonRPC /// The error field /// [JsonProperty("error", NullValueHandling = NullValueHandling.Ignore)] - public ErrorResponse Error { get; set; } + public Error Error { get; set; } /// /// Create a blank JSON rpc error response @@ -43,7 +43,7 @@ public JsonRpcError() /// /// The id of the response /// The error value - public JsonRpcError(long id, ErrorResponse error) + public JsonRpcError(long id, Error error) { Id = id; Error = error; diff --git a/Core Modules/WalletConnectSharp.Network/Models/JsonRpcResponse.cs b/Core Modules/WalletConnectSharp.Network/Models/JsonRpcResponse.cs index 30594e1..951740f 100644 --- a/Core Modules/WalletConnectSharp.Network/Models/JsonRpcResponse.cs +++ b/Core Modules/WalletConnectSharp.Network/Models/JsonRpcResponse.cs @@ -30,7 +30,7 @@ public string JsonRPC /// The error field for this response, if one is present /// [JsonProperty("error", NullValueHandling = NullValueHandling.Ignore)] - public ErrorResponse Error { get; set; } + public Error Error { get; set; } /// /// The result field for this response, if one is present @@ -64,7 +64,7 @@ public JsonRpcResponse() /// The id of this json response /// The error of this json response, if one is present /// The result of this json response, if one is present - public JsonRpcResponse(long id, ErrorResponse error, T result) + public JsonRpcResponse(long id, Error error, T result) { Id = id; Error = error; diff --git a/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs b/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs index 5925255..0144fba 100644 --- a/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs +++ b/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs @@ -226,7 +226,7 @@ public async void TestErrorResponses() async void OnPeerBOnAuthRequested(object sender, AuthRequest request) { - await PeerB.Respond(new ErrorResponse() { Error = new Network.Models.ErrorResponse() { Code = 14001, Message = "Can not login" }, Id = request.Id }, this._iss); + await PeerB.Respond(new ErrorResponse() { Error = new Network.Models.Error() { Code = 14001, Message = "Can not login" }, Id = request.Id }, this._iss); } void OnPeerAOnAuthResponded(object sender, AuthResponse response) { diff --git a/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthClientTest.cs b/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthClientTest.cs new file mode 100644 index 0000000..f84f19b --- /dev/null +++ b/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthClientTest.cs @@ -0,0 +1,524 @@ +using System.Text; +using NBitcoin; +using Nethereum.HdWallet; +using WalletConnectSharp.Auth.Interfaces; +using WalletConnectSharp.Auth.Internals; +using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Core.Models.Pairing; +using WalletConnectSharp.Core.Models.Publisher; +using WalletConnectSharp.Core.Models.Relay; +using WalletConnectSharp.Core.Models.Verify; +using WalletConnectSharp.Events; +using Xunit; +using ErrorResponse = WalletConnectSharp.Auth.Models.ErrorResponse; + +namespace WalletConnectSharp.Auth.Tests +{ + public class AuthClientTests : IClassFixture + { + private static readonly RequestParams DefaultRequestParams = new RequestParams() + { + Aud = "http://localhost:3000/login", + Domain = "localhost:3000", + ChainId = "eip155:1", + Nonce = CryptoUtils.GenerateNonce() + }; + + private Web3WalletFixture _authFixture; + private readonly string _iss; + private readonly Wallet _wallet; + + public IAuthClient PeerA + { + get + { + return _authFixture.ClientA; + } + } + + public IAuthClient PeerB + { + get + { + return _authFixture.ClientB; + } + } + + public string WalletAddress + { + get + { + return _wallet.GetAddresses(1)[0]; + } + } + + public AuthClientTests(Web3WalletFixture authFixture) + { + this._authFixture = authFixture; + + this._wallet = new Wallet(Wordlist.English, WordCount.Twelve); + this._iss = $"did:pkh:eip155:1:{this.WalletAddress}"; + } + + [Fact, Trait("Category", "unit")] + public async void TestInit() + { + await _authFixture.WaitForClientsReady(); + + Assert.NotNull(PeerA); + Assert.NotNull(PeerB); + + Assert.NotNull(PeerA.Core); + Assert.NotNull(PeerA.Events); + Assert.NotNull(PeerA.Core.Expirer); + Assert.NotNull(PeerA.Core.History); + Assert.NotNull(PeerA.Core.Pairing); + + Assert.NotNull(PeerB.Core); + Assert.NotNull(PeerB.Events); + Assert.NotNull(PeerB.Core.Expirer); + Assert.NotNull(PeerB.Core.History); + Assert.NotNull(PeerB.Core.Pairing); + } + + [Fact, Trait("Category", "unit")] + public async void TestPairs() + { + await _authFixture.WaitForClientsReady(); + + var ogPairSize = PeerA.Core.Pairing.Pairings.Length; + + TaskCompletionSource authRequested = new TaskCompletionSource(); + + void OnPeerBOnAuthRequested(object o, AuthRequest authRequest) => authRequested.SetResult(true); + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + var uriData = await PeerA.Request(DefaultRequestParams); + var uri = uriData.Uri; + + await PeerB.Core.Pairing.Pair(uri); + + await authRequested.Task; + + Assert.Equal(PeerA.Core.Pairing.Pairings.Select(p => p.Key), PeerB.Core.Pairing.Pairings.Select(p => p.Key)); + Assert.Equal(ogPairSize + 1, PeerA.Core.Pairing.Pairings.Length); + + var peerAHistory = await PeerA.Core.History.JsonRpcHistoryOfType(); + var peerBHistory = await PeerB.Core.History.JsonRpcHistoryOfType(); + + Assert.Equal(peerAHistory.Size, peerBHistory.Size); + + Assert.True(PeerB.Core.Pairing.Pairings[0].Active); + + // Cleanup event listeners + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + } + + [Fact, Trait("Category", "unit")] + public async void TestKnownPairings() + { + await _authFixture.WaitForClientsReady(); + + var ogSizeA = PeerA.Core.Pairing.Pairings.Length; + var history = await PeerA.AuthHistory(); + var ogHistorySizeA = history.Keys.Length; + + var ogSizeB = PeerB.Core.Pairing.Pairings.Length; + var historyB = await PeerB.AuthHistory(); + var ogHistorySizeB = historyB.Keys.Length; + + List responses = new List(); + TaskCompletionSource responseTask = new TaskCompletionSource(); + + async void OnPeerBOnAuthRequested(object sender, AuthRequest request) + { + var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this._iss); + var signature = await _wallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); + + await PeerB.Respond(new Cacao() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this._iss); + + Assert.Equal(Validation.Unknown, request.VerifyContext?.Validation); + } + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + void OnPeerAOnAuthResponded(object sender, AuthResponse args) + { + responses.Add(args); + responseTask.SetResult(args); + } + + PeerA.AuthResponded += OnPeerAOnAuthResponded; + + void OnPeerAOnAuthError(object sender, AuthErrorResponse args) + { + responses.Add(args); + responseTask.SetResult(args); + } + + PeerA.AuthError += OnPeerAOnAuthError; + + var requestData = await PeerA.Request(DefaultRequestParams); + + await PeerB.Core.Pairing.Pair(requestData.Uri); + + await responseTask.Task; + + // Reset + responseTask = new TaskCompletionSource(); + + // Get last pairing, that is the one we just made + var knownPairing = PeerA.Core.Pairing.Pairings[^1]; + + var requestData2 = await PeerA.Request(DefaultRequestParams, knownPairing.Topic); + + await responseTask.Task; + + Assert.Null(requestData2.Uri); + + Assert.Equal(ogSizeA + 1, PeerA.Core.Pairing.Pairings.Length); + Assert.Equal(ogHistorySizeA + 2, history.Keys.Length); + Assert.Equal(ogSizeB + 1, PeerB.Core.Pairing.Pairings.Length); + Assert.Equal(ogHistorySizeB + 2, historyB.Keys.Length); + Assert.Equal(responses[0].Topic, responses[1].Topic); + + // Cleanup event listeners + + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + PeerA.AuthResponded -= OnPeerAOnAuthResponded; + PeerA.AuthError -= OnPeerAOnAuthError; + } + + [Fact, Trait("Category", "unit")] + public async void HandlesAuthRequests() + { + await _authFixture.WaitForClientsReady(); + + var ogSize = PeerB.Requests.Length; + + TaskCompletionSource receivedAuthRequest = new TaskCompletionSource(); + + void OnPeerBOnAuthRequested(object o, AuthRequest authRequest) => receivedAuthRequest.SetResult(true); + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + var requestData = await PeerA.Request(DefaultRequestParams); + + await PeerB.Core.Pairing.Pair(requestData.Uri); + + await receivedAuthRequest.Task; + + Assert.Equal(ogSize + 1, PeerB.Requests.Length); + + // Cleanup event listeners + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + } + + [Fact, Trait("Category", "unit")] + public async void TestErrorResponses() + { + await _authFixture.WaitForClientsReady(); + + var ogPSize = PeerA.Core.Pairing.Pairings.Length; + + TaskCompletionSource errorResponse = new TaskCompletionSource(); + + async void OnPeerBOnAuthRequested(object sender, AuthRequest request) + { + await PeerB.Respond(new ErrorResponse() { Error = new Network.Models.Error() { Code = 14001, Message = "Can not login" }, Id = request.Id }, this._iss); + } + void OnPeerAOnAuthResponded(object sender, AuthResponse response) + { + errorResponse.SetResult(false); + } + + void OnPeerAOnAuthError(object sender, AuthErrorResponse response) => errorResponse.SetResult(true); + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + PeerA.AuthResponded += OnPeerAOnAuthResponded; + PeerA.AuthError += OnPeerAOnAuthError; + + var requestData = await PeerA.Request(DefaultRequestParams); + + Assert.Equal(ogPSize + 1, PeerA.Core.Pairing.Pairings.Length); + Assert.False(PeerA.Core.Pairing.Pairings[^1].Active); + + await PeerB.Core.Pairing.Pair(requestData.Uri); + + await errorResponse.Task; + + Assert.False(PeerA.Core.Pairing.Pairings[^1].Active); + Assert.True(errorResponse.Task.Result); + + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + PeerA.AuthResponded -= OnPeerAOnAuthResponded; + PeerA.AuthError -= OnPeerAOnAuthError; + } + + [Fact, Trait("Category", "unit")] + public async void HandlesSuccessfulResponse() + { + await _authFixture.WaitForClientsReady(); + + var ogPSize = PeerA.Core.Pairing.Pairings.Length; + + TaskCompletionSource successfulResponse = new TaskCompletionSource(); + + async void OnPeerBOnAuthRequested(object sender, AuthRequest request) + { + var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this._iss); + var signature = await _wallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); + + await PeerB.Respond(new ResultResponse() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this._iss); + + Assert.Equal(Validation.Unknown, request.VerifyContext?.Validation); + } + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + void OnPeerAOnAuthResponded(object sender, AuthResponse response) => successfulResponse.SetResult(response.Response.Result?.Signature != null); + + PeerA.AuthResponded += OnPeerAOnAuthResponded; + + void OnPeerAOnAuthError(object sender, AuthErrorResponse response) => successfulResponse.SetResult(false); + + PeerA.AuthError += OnPeerAOnAuthError; + + var requestData = await PeerA.Request(DefaultRequestParams); + + Assert.Equal(ogPSize + 1, PeerA.Core.Pairing.Pairings.Length); + Assert.False(PeerA.Core.Pairing.Pairings[^1].Active); + + await PeerB.Core.Pairing.Pair(requestData.Uri); + + await successfulResponse.Task; + + Assert.True(PeerA.Core.Pairing.Pairings[^1].Active); + Assert.True(successfulResponse.Task.Result); + + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + PeerA.AuthResponded -= OnPeerAOnAuthResponded; + PeerA.AuthError -= OnPeerAOnAuthError; + } + + [Fact, Trait("Category", "unit")] + public async void TestCustomRequestExpiry() + { + await _authFixture.WaitForClientsReady(); + + var uri = ""; + var expiry = 1000; + + TaskCompletionSource resolve1 = new TaskCompletionSource(); + + PeerA.Core.Relayer.Once(RelayerEvents.Publish, (sender, @event) => + { + Assert.Equal(expiry, @event.EventData.Options?.TTL); + resolve1.SetResult(true); + }); + + + await Task.WhenAll(resolve1.Task, Task.Run(async () => + { + var response = await PeerA.Request(new RequestParams(DefaultRequestParams) { Expiry = expiry }); + uri = response.Uri; + })); + + TaskCompletionSource resolve3 = new TaskCompletionSource(); + + async void OnPeerBOnAuthRequested(object sender, AuthRequest request) + { + var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this._iss); + var signature = await _wallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); + + await PeerB.Respond(new ResultResponse() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this._iss); + resolve3.SetResult(true); + } + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + await Task.WhenAll(resolve3.Task, PeerB.Core.Pairing.Pair(uri)); + + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + } + + [Fact, Trait("Category", "unit")] + public async void TestGetPendingPairings() + { + await _authFixture.WaitForClientsReady(); + + var ogCount = PeerB.PendingRequests.Count; + + TaskCompletionSource receivedAuthRequest = new TaskCompletionSource(); + var aud = "http://localhost:3000/login"; + + void OnPeerBOnAuthRequested(object sender, AuthRequest request) => receivedAuthRequest.SetResult(true); + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + var requestData = await PeerA.Request(DefaultRequestParams); + + await PeerB.Core.Pairing.Pair(requestData.Uri); + + await receivedAuthRequest.Task; + + var requests = PeerB.PendingRequests; + + Assert.Equal(ogCount + 1, requests.Count); + Assert.Contains(requests, r => r.Value.CacaoPayload.Aud == aud); + + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + } + + [Fact, Trait("Category", "unit")] + public async void TestGetPairings() + { + var peerAOgSize = PeerA.Core.Pairing.Pairings.Length; + var peerBOgSize = PeerB.Core.Pairing.Pairings.Length; + + TaskCompletionSource receivedAuthRequest = new TaskCompletionSource(); + + void OnPeerBOnAuthRequested(object sender, AuthRequest request) => receivedAuthRequest.SetResult(true); + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + var requestData = await PeerA.Request(DefaultRequestParams); + + await PeerB.Core.Pairing.Pair(requestData.Uri); + + await receivedAuthRequest.Task; + + var clientPairings = PeerA.Core.Pairing.Pairings; + var peerPairings = PeerB.Core.Pairing.Pairings; + + Assert.Equal(peerAOgSize + 1, clientPairings.Length); + Assert.Equal(peerBOgSize + 1, peerPairings.Length); + Assert.Equal(clientPairings[^1].Topic, peerPairings[^1].Topic); + + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + } + + [Fact, Trait("Category", "unit")] + public async void TestPing() + { + await _authFixture.WaitForClientsReady(); + + TaskCompletionSource receivedAuthRequest = new TaskCompletionSource(); + TaskCompletionSource receivedClientPing = new TaskCompletionSource(); + TaskCompletionSource receivedPeerPing = new TaskCompletionSource(); + + void OnPeerBOnAuthRequested(object sender, AuthRequest request) => receivedAuthRequest.SetResult(true); + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + PeerB.Core.Pairing.Once(PairingEvents.PairingPing, (sender, @event) => + { + receivedPeerPing.SetResult(true); + }); + + PeerA.Core.Pairing.Once(PairingEvents.PairingPing, (sender, @event) => + { + receivedClientPing.SetResult(true); + }); + + var requestData = await PeerA.Request(DefaultRequestParams); + + await PeerB.Core.Pairing.Pair(requestData.Uri); + + await receivedAuthRequest.Task; + + var pairing = PeerA.Core.Pairing.Pairings[^1]; + await PeerA.Core.Pairing.Ping(pairing.Topic); + await PeerB.Core.Pairing.Ping(pairing.Topic); + + await Task.WhenAll(receivedClientPing.Task, receivedPeerPing.Task); + + Assert.True(receivedClientPing.Task.Result); + Assert.True(receivedPeerPing.Task.Result); + + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + } + + [Fact, Trait("Category", "unit")] + public async void TestDisconnectedPairing() + { + await _authFixture.WaitForClientsReady(); + + var peerAOgSize = PeerA.Core.Pairing.Pairings.Length; + var peerBOgSize = PeerB.Core.Pairing.Pairings.Length; + + TaskCompletionSource receivedAuthRequest = new TaskCompletionSource(); + TaskCompletionSource peerDeletedPairing = new TaskCompletionSource(); + + void OnPeerBOnAuthRequested(object sender, AuthRequest request) => receivedAuthRequest.SetResult(true); + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + PeerB.Core.Pairing.Once(PairingEvents.PairingDelete, (sender, @event) => + { + peerDeletedPairing.SetResult(true); + }); + + var requestData = await PeerA.Request(DefaultRequestParams); + + await PeerB.Core.Pairing.Pair(requestData.Uri); + + await receivedAuthRequest.Task; + + var clientPairings = PeerA.Core.Pairing.Pairings; + var peerPairings = PeerB.Core.Pairing.Pairings; + + Assert.Equal(peerAOgSize + 1, PeerA.Core.Pairing.Pairings.Length); + Assert.Equal(peerBOgSize + 1, PeerB.Core.Pairing.Pairings.Length); + Assert.Equal(clientPairings[^1].Topic, peerPairings[^1].Topic); + + await PeerA.Core.Pairing.Disconnect(clientPairings[^1].Topic); + + await peerDeletedPairing.Task; + Assert.Equal(peerAOgSize, PeerA.Core.Pairing.Pairings.Length); + Assert.Equal(peerBOgSize, PeerB.Core.Pairing.Pairings.Length); + + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + } + + [Fact, Trait("Category", "unit")] + public async void TestReceivesMetadata() + { + await _authFixture.WaitForClientsReady(); + + var receivedMetadataName = ""; + var ogPairingSize = PeerA.Core.Pairing.Pairings.Length; + + TaskCompletionSource hasResponded = new TaskCompletionSource(); + + async void OnPeerBOnAuthRequested(object sender, AuthRequest request) + { + receivedMetadataName = request.Parameters.Requester?.Metadata?.Name; + var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this._iss); + var signature = await _wallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); + + await PeerB.Respond(new ResultResponse() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this._iss); + + hasResponded.SetResult(true); + Assert.Equal(Validation.Unknown, request.VerifyContext.Validation); + } + + PeerB.AuthRequested += OnPeerBOnAuthRequested; + + var requestData = await PeerA.Request(DefaultRequestParams); + + Assert.Equal(ogPairingSize + 1, PeerA.Core.Pairing.Pairings.Length); + Assert.False(PeerA.Core.Pairing.Pairings[^1].Active); + + await PeerB.Core.Pairing.Pair(requestData.Uri); + + await hasResponded.Task; + + Assert.True(PeerA.Core.Pairing.Pairings[^1].Active); + Assert.True(hasResponded.Task.Result); + Assert.Equal(PeerA.Metadata.Name, receivedMetadataName); + PeerB.AuthRequested -= OnPeerBOnAuthRequested; + } + } +} diff --git a/Tests/WalletConnectSharp.Web3Wallet.Tests/WalletConnectSharp.Web3Wallet.Tests.csproj b/Tests/WalletConnectSharp.Web3Wallet.Tests/WalletConnectSharp.Web3Wallet.Tests.csproj new file mode 100644 index 0000000..dbf1d2d --- /dev/null +++ b/Tests/WalletConnectSharp.Web3Wallet.Tests/WalletConnectSharp.Web3Wallet.Tests.csproj @@ -0,0 +1,29 @@ + + + + net6.0;netcoreapp3.1 + 2.0.0 + 2.0.0 + 2.0.0 + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/Tests/WalletConnectSharp.Web3Wallet.Tests/Web3WalletFixture.cs b/Tests/WalletConnectSharp.Web3Wallet.Tests/Web3WalletFixture.cs new file mode 100644 index 0000000..96c4b53 --- /dev/null +++ b/Tests/WalletConnectSharp.Web3Wallet.Tests/Web3WalletFixture.cs @@ -0,0 +1,63 @@ +using WalletConnectSharp.Core.Interfaces; +using WalletConnectSharp.Core.Models; +using WalletConnectSharp.Storage; +using WalletConnectSharp.Tests.Common; +using WalletConnectSharp.Web3Wallet.Interfaces; +using WalletConnectSharp.Web3Wallet; + +namespace WalletConnectSharp.Auth.Tests; + +public class Web3WalletFixture : TwoClientsFixture +{ + public CoreOptions OptionsA { get; protected set; } + + public CoreOptions OptionsB { get; protected set; } + + public AuthMetadata MetadataA { get; protected set; } + + public AuthMetadata MetadataB { get; protected set; } + + public ICore CoreA { get; protected set; } + + public ICore CoreB { get; protected set; } + + protected override async void Init() + { + MetadataA = new AuthMetadata() + { + Description = "An example dapp to showcase WalletConnectSharpv2", + Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, + Name = "WalletConnectSharpv2 Dapp Example", + Url = "https://walletconnect.com" + }; + + OptionsA = new CoreOptions() + { + ProjectId = TestValues.TestProjectId, + RelayUrl = TestValues.TestRelayUrl, + // Omit if you want persistant storage + Storage = new InMemoryStorage(), + }; + + OptionsB = new CoreOptions() + { + ProjectId = TestValues.TestProjectId, + RelayUrl = TestValues.TestRelayUrl, + // Omit if you want persistant storage + Storage = new InMemoryStorage() + }; + + MetadataB = new AuthMetadata() + { + Description = "An example wallet to showcase WalletConnectSharpv2", + Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, + Name = "WalletConnectSharpv2 Wallet Example", + Url = "https://walletconnect.com" + }; + + CoreA = new Core.Core(OptionsA); + + ClientA = await Web3WalletClient.Init(OptionsA); + ClientB = await Web3WalletClient.Init(OptionsB); + } +} diff --git a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs index b853c27..82acf9c 100644 --- a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs +++ b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs @@ -10,7 +10,6 @@ using WalletConnectSharp.Events; using WalletConnectSharp.Events.Model; using WalletConnectSharp.Network.Models; -using CommonErrorResponse = WalletConnectSharp.Network.Models.ErrorResponse; using ErrorResponse = WalletConnectSharp.Auth.Models.ErrorResponse; namespace WalletConnectSharp.Auth.Controllers; @@ -276,7 +275,7 @@ private async Task OnAuthResponse(string topic, JsonRpcResponse response) { this.Client.OnAuthResponse(new AuthErrorResponse() { - Id = id, Topic = topic, Error = CommonErrorResponse.FromErrorType(ErrorType.GENERIC, new + Id = id, Topic = topic, Error = Error.FromErrorType(ErrorType.GENERIC, new { Message = "Invalid signature" }) diff --git a/WalletConnectSharp.Auth/Models/AuthErrorResponse.cs b/WalletConnectSharp.Auth/Models/AuthErrorResponse.cs index 68358f1..ba614dc 100644 --- a/WalletConnectSharp.Auth/Models/AuthErrorResponse.cs +++ b/WalletConnectSharp.Auth/Models/AuthErrorResponse.cs @@ -1,10 +1,10 @@ using Newtonsoft.Json; -using CommonErrorResponse = WalletConnectSharp.Network.Models.ErrorResponse; +using WalletConnectSharp.Network.Models; namespace WalletConnectSharp.Auth.Models; public class AuthErrorResponse : TopicMessage { [JsonProperty("params")] - public CommonErrorResponse Error { get; set; } + public Error Error { get; set; } } diff --git a/WalletConnectSharp.Auth/Models/ErrorResponse.cs b/WalletConnectSharp.Auth/Models/ErrorResponse.cs index 08aa465..af80776 100644 --- a/WalletConnectSharp.Auth/Models/ErrorResponse.cs +++ b/WalletConnectSharp.Auth/Models/ErrorResponse.cs @@ -1,9 +1,10 @@ using Newtonsoft.Json; -using CommonErrorResponse = WalletConnectSharp.Network.Models.ErrorResponse; +using WalletConnectSharp.Network.Models; + namespace WalletConnectSharp.Auth.Models; public class ErrorResponse : Message { [JsonProperty("error")] - public CommonErrorResponse Error { get; set; } + public Error Error { get; set; } } diff --git a/WalletConnectSharp.Core/Controllers/Pairing.cs b/WalletConnectSharp.Core/Controllers/Pairing.cs index 2030d15..19da8f0 100644 --- a/WalletConnectSharp.Core/Controllers/Pairing.cs +++ b/WalletConnectSharp.Core/Controllers/Pairing.cs @@ -324,7 +324,7 @@ public async Task Disconnect(string topic) if (Store.Keys.Contains(topic)) { - var error = ErrorResponse.FromErrorType(ErrorType.USER_DISCONNECTED); + var error = Error.FromErrorType(ErrorType.USER_DISCONNECTED); await Core.MessageHandler.SendRequest(topic, new PairingDelete() {Code = error.Code, Message = error.Message}); await DeletePairing(topic); @@ -351,7 +351,7 @@ private async Task DeletePairing(string topic) await this.Core.Relayer.Unsubscribe(topic); await Task.WhenAll( - pairingHasDeleted ? Task.CompletedTask : this.Store.Delete(topic, ErrorResponse.FromErrorType(ErrorType.USER_DISCONNECTED)), + pairingHasDeleted ? Task.CompletedTask : this.Store.Delete(topic, Error.FromErrorType(ErrorType.USER_DISCONNECTED)), symKeyHasDeleted ? Task.CompletedTask : this.Core.Crypto.DeleteSymKey(topic), expirerHasDeleted ? Task.CompletedTask : this.Core.Expirer.Delete(topic) ); @@ -430,7 +430,7 @@ private async Task OnPairingPingRequest(string topic, JsonRpcRequest(id, topic, ErrorResponse.FromException(e)); + await Core.MessageHandler.SendError(id, topic, Error.FromException(e)); } } @@ -462,11 +462,11 @@ private async Task OnPairingDeleteRequest(string topic, JsonRpcRequest(id, topic, ErrorResponse.FromException(e)); + await Core.MessageHandler.SendError(id, topic, Error.FromException(e)); } } - private async Task IsValidDisconnect(string topic, ErrorResponse reason) + private async Task IsValidDisconnect(string topic, Error reason) { if (string.IsNullOrWhiteSpace(topic)) { diff --git a/WalletConnectSharp.Core/Controllers/Store.cs b/WalletConnectSharp.Core/Controllers/Store.cs index 08ea551..a1f3d53 100644 --- a/WalletConnectSharp.Core/Controllers/Store.cs +++ b/WalletConnectSharp.Core/Controllers/Store.cs @@ -227,7 +227,7 @@ public Task Update(TKey key, TValue update) /// The key to delete /// The reason this key was deleted using an ErrorResponse /// - public Task Delete(TKey key, ErrorResponse reason) + public Task Delete(TKey key, Error reason) { IsInitialized(); diff --git a/WalletConnectSharp.Core/Controllers/Subscriber.cs b/WalletConnectSharp.Core/Controllers/Subscriber.cs index 9f709ac..30fd81c 100644 --- a/WalletConnectSharp.Core/Controllers/Subscriber.cs +++ b/WalletConnectSharp.Core/Controllers/Subscriber.cs @@ -395,7 +395,7 @@ protected virtual void OnResubscribe(string id, PendingSubscription @params) pending.Remove(@params.Topic); } - protected virtual async Task OnUnsubscribe(string topic, string id, ErrorResponse reason) + protected virtual async Task OnUnsubscribe(string topic, string id, Error reason) { // TODO Figure out how to do this //Events.RemoveListener(id); @@ -445,7 +445,7 @@ protected virtual Task UnsubscribeByTopic(string topic, UnsubscribeOptions opts ); } - protected virtual void DeleteSubscription(string id, ErrorResponse reason) + protected virtual void DeleteSubscription(string id, Error reason) { var subscription = GetSubscription(id); _subscriptions.Remove(id); @@ -474,7 +474,7 @@ protected virtual async Task UnsubscribeById(string topic, string id, Unsubscrib } await RpcUnsubscribe(topic, id, opts.Relay); - ErrorResponse reason = null; + Error reason = null; await OnUnsubscribe(topic, id, reason); } diff --git a/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs b/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs index 86cd1e9..aa9e4c4 100644 --- a/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs +++ b/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs @@ -336,7 +336,7 @@ public async Task SendResult(long id, string topic, TR result, EncodeOpti /// The error response to send /// The request type /// The response type - public async Task SendError(long id, string topic, ErrorResponse error, EncodeOptions options = null) + public async Task SendError(long id, string topic, Error error, EncodeOptions options = null) { var payload = new JsonRpcResponse(id, error, default); var message = await this.Core.Crypto.Encode(topic, payload, options); diff --git a/WalletConnectSharp.Core/Interfaces/IStore.cs b/WalletConnectSharp.Core/Interfaces/IStore.cs index 7db3106..0cb0597 100644 --- a/WalletConnectSharp.Core/Interfaces/IStore.cs +++ b/WalletConnectSharp.Core/Interfaces/IStore.cs @@ -64,7 +64,7 @@ public interface IStore : IModule where TValue : IKeyHolder /// The key to delete /// The reason this key was deleted using an ErrorResponse /// - public Task Delete(TKey key, ErrorResponse reason); + public Task Delete(TKey key, Error reason); public IDictionary ToDictionary(); } diff --git a/WalletConnectSharp.Core/Interfaces/ITypedMessageHandler.cs b/WalletConnectSharp.Core/Interfaces/ITypedMessageHandler.cs index 4d01ed5..5a7236d 100644 --- a/WalletConnectSharp.Core/Interfaces/ITypedMessageHandler.cs +++ b/WalletConnectSharp.Core/Interfaces/ITypedMessageHandler.cs @@ -126,6 +126,6 @@ void HandleMessageType(Func, Task> requestCallb /// (optional) Crypto Encoding options /// The request type /// The response type - Task SendError(long id, string topic, ErrorResponse error, EncodeOptions options = null); + Task SendError(long id, string topic, Error error, EncodeOptions options = null); } } diff --git a/WalletConnectSharp.Core/Models/MessageHandler/RequestEventArgs.cs b/WalletConnectSharp.Core/Models/MessageHandler/RequestEventArgs.cs index 3305d74..92ed156 100644 --- a/WalletConnectSharp.Core/Models/MessageHandler/RequestEventArgs.cs +++ b/WalletConnectSharp.Core/Models/MessageHandler/RequestEventArgs.cs @@ -37,7 +37,7 @@ public class RequestEventArgs /// to send an Error response when this event completes. /// This value will always override if the value is non-null /// - public ErrorResponse Error { get; set; } + public Error Error { get; set; } internal RequestEventArgs(string topic, JsonRpcRequest request) { diff --git a/WalletConnectSharp.Core/Models/Pairing/Methods/PairingDelete.cs b/WalletConnectSharp.Core/Models/Pairing/Methods/PairingDelete.cs index 07f6130..6429657 100644 --- a/WalletConnectSharp.Core/Models/Pairing/Methods/PairingDelete.cs +++ b/WalletConnectSharp.Core/Models/Pairing/Methods/PairingDelete.cs @@ -9,7 +9,7 @@ namespace WalletConnectSharp.Core.Models.Pairing.Methods [RpcMethod("wc_pairingDelete")] [RpcRequestOptions(Clock.ONE_DAY, 1000)] [RpcResponseOptions(Clock.ONE_DAY, 1001)] - public class PairingDelete : ErrorResponse + public class PairingDelete : Error { } } diff --git a/WalletConnectSharp.Core/Models/Subscriber/DeletedSubscription.cs b/WalletConnectSharp.Core/Models/Subscriber/DeletedSubscription.cs index bfc28d4..ff38c80 100644 --- a/WalletConnectSharp.Core/Models/Subscriber/DeletedSubscription.cs +++ b/WalletConnectSharp.Core/Models/Subscriber/DeletedSubscription.cs @@ -12,6 +12,6 @@ public class DeletedSubscription : ActiveSubscription /// The reason why the subscription was deleted /// [JsonProperty("reason")] - public ErrorResponse Reason { get; set; } + public Error Reason { get; set; } } } diff --git a/WalletConnectSharp.Sign/Engine.cs b/WalletConnectSharp.Sign/Engine.cs index 617b856..25a51e4 100644 --- a/WalletConnectSharp.Sign/Engine.cs +++ b/WalletConnectSharp.Sign/Engine.cs @@ -405,7 +405,7 @@ await MessageHandler.SendResult(id, pair }, ResponderPublicKey = selfPublicKey }); - await this.Client.Proposal.Delete(id, ErrorResponse.FromErrorType(ErrorType.USER_DISCONNECTED)); + await this.Client.Proposal.Delete(id, Error.FromErrorType(ErrorType.USER_DISCONNECTED)); await this.Client.Core.Pairing.Activate(pairingTopic); } @@ -415,7 +415,7 @@ await MessageHandler.SendResult(id, pair /// /// Reject a proposal that was recently paired. If the given proposal was not from a recent pairing, /// or the proposal has expired, then an Exception will be thrown. - /// Use or + /// Use or /// to generate a object, or use the alias function /// /// The parameters of the rejection @@ -432,7 +432,7 @@ public async Task Reject(RejectParams @params) if (!string.IsNullOrWhiteSpace(pairingTopic)) { await MessageHandler.SendError(id, pairingTopic, reason); - await this.Client.Proposal.Delete(id, ErrorResponse.FromErrorType(ErrorType.USER_DISCONNECTED)); + await this.Client.Proposal.Delete(id, Error.FromErrorType(ErrorType.USER_DISCONNECTED)); } } @@ -633,10 +633,10 @@ 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, ErrorResponse reason) + public async Task Disconnect(string topic, Error reason) { IsInitialized(); - var error = reason ?? ErrorResponse.FromErrorType(ErrorType.USER_DISCONNECTED); + var error = reason ?? Error.FromErrorType(ErrorType.USER_DISCONNECTED); await PrivateThis.IsValidDisconnect(topic, error); if (this.Client.Session.Keys.Contains(topic)) @@ -695,7 +695,7 @@ public Task Reject(ProposalStruct proposalStruct, string message = null) /// /// The proposal to reject /// An error explaining the reason for the rejection - public Task Reject(ProposalStruct proposalStruct, ErrorResponse error) + public Task Reject(ProposalStruct proposalStruct, Error error) { return Reject(proposalStruct.RejectProposal(error)); } diff --git a/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs b/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs index 3e66496..0273c4a 100644 --- a/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs +++ b/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs @@ -56,7 +56,7 @@ public interface IEngineAPI /// /// Reject a proposal that was recently paired. If the given proposal was not from a recent pairing, /// or the proposal has expired, then an Exception will be thrown. - /// Use or + /// Use or /// to generate a object, or use the alias function /// /// The parameters of the rejection @@ -77,7 +77,7 @@ public interface IEngineAPI /// /// The proposal to reject /// An error explaining the reason for the rejection - Task Reject(ProposalStruct proposalStruct, ErrorResponse error); + Task Reject(ProposalStruct proposalStruct, Error error); /// /// Update a session, adding/removing additional namespaces in the given topic. @@ -146,7 +146,7 @@ public interface IEngineAPI /// /// The topic of the session to disconnect /// An (optional) error reason for the disconnect - Task Disconnect(string topic, ErrorResponse reason = null); + Task Disconnect(string topic, Error reason = null); /// /// Find all sessions that have a namespace that match the given diff --git a/WalletConnectSharp.Sign/Interfaces/IEnginePrivate.cs b/WalletConnectSharp.Sign/Interfaces/IEnginePrivate.cs index e97ba31..7c16abb 100644 --- a/WalletConnectSharp.Sign/Interfaces/IEnginePrivate.cs +++ b/WalletConnectSharp.Sign/Interfaces/IEnginePrivate.cs @@ -67,9 +67,9 @@ internal interface IEnginePrivate internal Task IsValidEmit(string topic, EventData request, string chainId); - internal Task IsValidDisconnect(string topic, ErrorResponse reason); + internal Task IsValidDisconnect(string topic, Error reason); - internal Task DeletePendingSessionRequest(long id, ErrorResponse reason, bool expirerHasDeleted = false); + internal Task DeletePendingSessionRequest(long id, Error reason, bool expirerHasDeleted = false); internal Task SetPendingSessionRequest(PendingRequestStruct pendingRequest); } diff --git a/WalletConnectSharp.Sign/Internals/EngineHandler.cs b/WalletConnectSharp.Sign/Internals/EngineHandler.cs index 8ab0027..2dda682 100644 --- a/WalletConnectSharp.Sign/Internals/EngineHandler.cs +++ b/WalletConnectSharp.Sign/Internals/EngineHandler.cs @@ -21,7 +21,7 @@ async void ExpiredCallback(object sender, GenericEvent e) if (target.Id != null && this.Client.PendingRequests.Keys.Contains((long)target.Id)) { await PrivateThis.DeletePendingSessionRequest((long)target.Id, - ErrorResponse.FromErrorType(ErrorType.EXPIRED), true); + Error.FromErrorType(ErrorType.EXPIRED), true); return; } @@ -70,7 +70,7 @@ async Task IEnginePrivate.OnSessionProposeRequest(string topic, JsonRpcRequest(id, topic, - ErrorResponse.FromException(e)); + Error.FromException(e)); } } @@ -79,7 +79,7 @@ async Task IEnginePrivate.OnSessionProposeResponse(string topic, JsonRpcResponse var id = payload.Id; if (payload.IsError) { - await this.Client.Proposal.Delete(id, ErrorResponse.FromErrorType(ErrorType.USER_DISCONNECTED)); + await this.Client.Proposal.Delete(id, Error.FromErrorType(ErrorType.USER_DISCONNECTED)); this.Events.Trigger(EngineEvents.SessionConnect, payload); } else @@ -134,7 +134,7 @@ async Task IEnginePrivate.OnSessionSettleRequest(string topic, JsonRpcRequest(id, topic, ErrorResponse.FromException(e)); + await MessageHandler.SendError(id, topic, Error.FromException(e)); } } @@ -143,7 +143,7 @@ async Task IEnginePrivate.OnSessionSettleResponse(string topic, JsonRpcResponse< var id = payload.Id; if (payload.IsError) { - await this.Client.Session.Delete(topic, ErrorResponse.FromErrorType(ErrorType.USER_DISCONNECTED)); + await this.Client.Session.Delete(topic, Error.FromErrorType(ErrorType.USER_DISCONNECTED)); this.Events.Trigger($"session_approve{id}", payload); } else @@ -179,7 +179,7 @@ async Task IEnginePrivate.OnSessionUpdateRequest(string topic, JsonRpcRequest(id, topic, ErrorResponse.FromException(e)); + await MessageHandler.SendError(id, topic, Error.FromException(e)); } } @@ -205,7 +205,7 @@ async Task IEnginePrivate.OnSessionExtendRequest(string topic, JsonRpcRequest(id, topic, ErrorResponse.FromException(e)); + await MessageHandler.SendError(id, topic, Error.FromException(e)); } } @@ -230,7 +230,7 @@ async Task IEnginePrivate.OnSessionPingRequest(string topic, JsonRpcRequest(id, topic, ErrorResponse.FromException(e)); + await MessageHandler.SendError(id, topic, Error.FromException(e)); } } @@ -262,7 +262,7 @@ async Task IEnginePrivate.OnSessionDeleteRequest(string topic, JsonRpcRequest(id, topic, ErrorResponse.FromException(e)); + await MessageHandler.SendError(id, topic, Error.FromException(e)); } } @@ -283,7 +283,7 @@ async Task IEnginePrivate.OnSessionRequest(string topic, JsonRpcRequest, TR>(id, topic, ErrorResponse.FromException(e)); + await MessageHandler.SendError, TR>(id, topic, Error.FromException(e)); } } @@ -303,7 +303,7 @@ async Task IEnginePrivate.OnSessionEventRequest(string topic, JsonRpcRequest< } catch (WalletConnectException e) { - await MessageHandler.SendError, object>(id, topic, ErrorResponse.FromException(e)); + await MessageHandler.SendError, object>(id, topic, Error.FromException(e)); } } } diff --git a/WalletConnectSharp.Sign/Internals/EngineTasks.cs b/WalletConnectSharp.Sign/Internals/EngineTasks.cs index 8511343..95d0da4 100644 --- a/WalletConnectSharp.Sign/Internals/EngineTasks.cs +++ b/WalletConnectSharp.Sign/Internals/EngineTasks.cs @@ -15,7 +15,7 @@ namespace WalletConnectSharp.Sign { public partial class Engine { - async Task IEnginePrivate.DeletePendingSessionRequest(long id, ErrorResponse reason, bool expirerHasDeleted = false) + async Task IEnginePrivate.DeletePendingSessionRequest(long id, Error reason, bool expirerHasDeleted = false) { await Task.WhenAll( this.Client.PendingRequests.Delete(id, reason), @@ -48,7 +48,7 @@ async Task IEnginePrivate.DeleteSession(string topic) await this.Client.Core.Relayer.Unsubscribe(topic); await Task.WhenAll( - sessionDeleted ? Task.CompletedTask : this.Client.Session.Delete(topic, ErrorResponse.FromErrorType(ErrorType.USER_DISCONNECTED)), + sessionDeleted ? Task.CompletedTask : this.Client.Session.Delete(topic, Error.FromErrorType(ErrorType.USER_DISCONNECTED)), hasKeypairDeleted ? Task.CompletedTask : this.Client.Core.Crypto.DeleteKeyPair(self.PublicKey), hasSymkeyDeleted ? Task.CompletedTask : this.Client.Core.Crypto.DeleteSymKey(topic), expirerHasDeleted ? Task.CompletedTask : this.Client.Core.Expirer.Delete(topic) @@ -61,7 +61,7 @@ Task IEnginePrivate.DeleteProposal(long id) bool proposalHasDeleted = !this.Client.Proposal.Keys.Contains(id); return Task.WhenAll( - proposalHasDeleted ? Task.CompletedTask : this.Client.Proposal.Delete(id, ErrorResponse.FromErrorType(ErrorType.USER_DISCONNECTED)), + proposalHasDeleted ? Task.CompletedTask : this.Client.Proposal.Delete(id, Error.FromErrorType(ErrorType.USER_DISCONNECTED)), expirerHasDeleted ? Task.CompletedTask : this.Client.Core.Expirer.Delete(id) ); } diff --git a/WalletConnectSharp.Sign/Internals/EngineValidation.cs b/WalletConnectSharp.Sign/Internals/EngineValidation.cs index a7ed42d..0a19a81 100644 --- a/WalletConnectSharp.Sign/Internals/EngineValidation.cs +++ b/WalletConnectSharp.Sign/Internals/EngineValidation.cs @@ -287,7 +287,7 @@ async Task IEnginePrivate.IsValidEmit(string topic, EventData @event, stri } } - async Task IEnginePrivate.IsValidDisconnect(string topic, ErrorResponse reason) + async Task IEnginePrivate.IsValidDisconnect(string topic, Error reason) { if (string.IsNullOrWhiteSpace(topic)) { @@ -311,9 +311,9 @@ private bool IsValidAccountId(string account) return false; } - private ErrorResponse IsValidAccounts(string[] accounts, string context) + private Error IsValidAccounts(string[] accounts, string context) { - ErrorResponse error = null; + Error error = null; foreach (var account in accounts) { if (error != null) @@ -321,16 +321,16 @@ private ErrorResponse IsValidAccounts(string[] accounts, string context) if (!IsValidAccountId(account)) { - error = ErrorResponse.FromErrorType(ErrorType.UNSUPPORTED_ACCOUNTS, $"{context}, account {account} should be a string and conform to 'namespace:chainId:address' format"); + error = Error.FromErrorType(ErrorType.UNSUPPORTED_ACCOUNTS, $"{context}, account {account} should be a string and conform to 'namespace:chainId:address' format"); } } return error; } - private ErrorResponse IsValidNamespaceAccounts(Namespaces namespaces, string method) + private Error IsValidNamespaceAccounts(Namespaces namespaces, string method) { - ErrorResponse error = null; + Error error = null; foreach (var ns in namespaces.Values) { if (error != null) break; @@ -345,9 +345,9 @@ private ErrorResponse IsValidNamespaceAccounts(Namespaces namespaces, string met return error; } - private ErrorResponse IsValidNamespaces(Namespaces namespaces, string method) + private Error IsValidNamespaces(Namespaces namespaces, string method) { - ErrorResponse error = null; + Error error = null; if (namespaces != null) { var validAccountsError = IsValidNamespaceAccounts(namespaces, method); @@ -358,7 +358,7 @@ private ErrorResponse IsValidNamespaces(Namespaces namespaces, string method) } else { - error = ErrorResponse.FromErrorType(ErrorType.MISSING_OR_INVALID, $"{method}, namespaces should be an object with data"); + error = Error.FromErrorType(ErrorType.MISSING_OR_INVALID, $"{method}, namespaces should be an object with data"); } return error; @@ -407,15 +407,15 @@ private bool IsValidNamespacesChainId(Namespaces namespaces, string chainId) return true; } - private ErrorResponse IsConformingNamespaces(RequiredNamespaces requiredNamespaces, Namespaces namespaces, + private Error IsConformingNamespaces(RequiredNamespaces requiredNamespaces, Namespaces namespaces, string context) { - ErrorResponse error = null; + Error error = null; var requiredNamespaceKeys = requiredNamespaces.Keys.ToArray(); var namespaceKeys = namespaces.Keys.ToArray(); if (!HasOverlap(requiredNamespaceKeys, namespaceKeys)) - error = ErrorResponse.FromErrorType(ErrorType.NON_CONFORMING_NAMESPACES, $"{context} namespaces keys don't satisfy requiredNamespaces"); + error = Error.FromErrorType(ErrorType.NON_CONFORMING_NAMESPACES, $"{context} namespaces keys don't satisfy requiredNamespaces"); else { foreach (var key in requiredNamespaceKeys) @@ -428,15 +428,15 @@ private ErrorResponse IsConformingNamespaces(RequiredNamespaces requiredNamespac if (!HasOverlap(requiredNamespaceChains, namespaceChains)) { - error = ErrorResponse.FromErrorType(ErrorType.NON_CONFORMING_NAMESPACES, $"{context} namespaces accounts don't satisfy requiredNamespaces chains for {key}"); + 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 = ErrorResponse.FromErrorType(ErrorType.NON_CONFORMING_NAMESPACES, $"{context} namespaces methods don't satisfy requiredNamespaces methods for {key}"); + 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 = ErrorResponse.FromErrorType(ErrorType.NON_CONFORMING_NAMESPACES, $"{context} namespaces events don't satisfy requiredNamespaces events for {key}"); + error = Error.FromErrorType(ErrorType.NON_CONFORMING_NAMESPACES, $"{context} namespaces events don't satisfy requiredNamespaces events for {key}"); } } } diff --git a/WalletConnectSharp.Sign/Models/Engine/Methods/SessionDelete.cs b/WalletConnectSharp.Sign/Models/Engine/Methods/SessionDelete.cs index 0158507..d5e5408 100644 --- a/WalletConnectSharp.Sign/Models/Engine/Methods/SessionDelete.cs +++ b/WalletConnectSharp.Sign/Models/Engine/Methods/SessionDelete.cs @@ -12,7 +12,7 @@ namespace WalletConnectSharp.Sign.Models.Engine.Methods [RpcMethod("wc_sessionDelete")] [RpcRequestOptions(Clock.ONE_DAY, 1112)] [RpcResponseOptions(Clock.ONE_DAY, 1113)] - public class SessionDelete : ErrorResponse, IWcMethod + public class SessionDelete : Error, IWcMethod { } } diff --git a/WalletConnectSharp.Sign/Models/Engine/RejectParams.cs b/WalletConnectSharp.Sign/Models/Engine/RejectParams.cs index 5765cc2..e899b60 100644 --- a/WalletConnectSharp.Sign/Models/Engine/RejectParams.cs +++ b/WalletConnectSharp.Sign/Models/Engine/RejectParams.cs @@ -5,7 +5,7 @@ namespace WalletConnectSharp.Sign.Models.Engine { /// /// A class that represents parameters for rejecting a session proposal. Contains the id - /// of the session proposal to reject and the reason the session + /// of the session proposal to reject and the reason the session /// proposal was rejected /// public class RejectParams @@ -17,9 +17,9 @@ public class RejectParams public long Id { get; set; } /// - /// The reason the session proposal was rejected, as an + /// The reason the session proposal was rejected, as an /// [JsonProperty("reason")] - public ErrorResponse Reason { get; set; } + public Error Reason { get; set; } } } diff --git a/WalletConnectSharp.Sign/Models/ProposalStruct.cs b/WalletConnectSharp.Sign/Models/ProposalStruct.cs index 4e2e49e..a75d6a2 100644 --- a/WalletConnectSharp.Sign/Models/ProposalStruct.cs +++ b/WalletConnectSharp.Sign/Models/ProposalStruct.cs @@ -151,13 +151,13 @@ public ApproveParams ApproveProposal(string[] approvedAccounts, ProtocolOptions } /// - /// Reject this proposal with the given . This + /// Reject this proposal with the given . This /// will return a which must be used in /// /// The error reason this proposal was rejected /// A new object which must be used in /// If this proposal has no Id - public RejectParams RejectProposal(ErrorResponse error) + public RejectParams RejectProposal(Error error) { if (Id == null) throw new Exception("Proposal has no set Id"); @@ -180,7 +180,7 @@ public RejectParams RejectProposal(string message = null) if (message == null) message = "Proposal denied by remote host"; - return RejectProposal(new ErrorResponse() + return RejectProposal(new Error() { Message = message, Code = (long) ErrorType.USER_DISCONNECTED diff --git a/WalletConnectSharp.Sign/WalletConnectSignClient.cs b/WalletConnectSharp.Sign/WalletConnectSignClient.cs index e531c2f..d89852f 100644 --- a/WalletConnectSharp.Sign/WalletConnectSignClient.cs +++ b/WalletConnectSharp.Sign/WalletConnectSignClient.cs @@ -242,7 +242,7 @@ public Task Approve(ProposalStruct proposalStruct, params string[ /// /// Reject a proposal that was recently paired. If the given proposal was not from a recent pairing, /// or the proposal has expired, then an Exception will be thrown. - /// Use or + /// Use or /// to generate a object, or use the alias function /// /// The parameters of the rejection @@ -266,7 +266,7 @@ public Task Reject(ProposalStruct proposalStruct, string message = null) if (message == null) message = "Proposal denied by remote host"; - return Reject(proposalStruct, new ErrorResponse() + return Reject(proposalStruct, new Error() { Message = message, Code = (long) ErrorType.USER_DISCONNECTED, @@ -279,7 +279,7 @@ public Task Reject(ProposalStruct proposalStruct, string message = null) /// /// The proposal to reject /// An error explaining the reason for the rejection - public Task Reject(ProposalStruct proposalStruct, ErrorResponse error) + public Task Reject(ProposalStruct proposalStruct, Error error) { if (proposalStruct.Id == null) throw new ArgumentException("No proposal Id given"); @@ -377,7 +377,7 @@ public Task Ping(string topic) /// /// The topic of the session to disconnect /// An (optional) error reason for the disconnect - public Task Disconnect(string topic, ErrorResponse reason) + public Task Disconnect(string topic, Error reason) { return Engine.Disconnect(topic, reason); } diff --git a/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs b/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs index 06eb6de..6ded19a 100644 --- a/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs +++ b/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs @@ -12,7 +12,6 @@ using WalletConnectSharp.Sign.Models.Engine; using WalletConnectSharp.Sign.Models.Engine.Events; using WalletConnectSharp.Web3Wallet.Interfaces; -using ErrorResponse = WalletConnectSharp.Network.Models.ErrorResponse; namespace WalletConnectSharp.Web3Wallet.Controllers; @@ -102,12 +101,12 @@ public Task ApproveSession(ProposalStruct proposal, params string return ApproveSession(param.Id, param.Namespaces, param.RelayProtocol); } - public Task RejectSession(long id, ErrorResponse reason) + public Task RejectSession(long id, Error reason) { return this.SignClient.Reject(new RejectParams() { Id = id, Reason = reason }); } - public Task RejectSession(ProposalStruct proposal, ErrorResponse reason) + public Task RejectSession(ProposalStruct proposal, Error reason) { var parm = proposal.RejectProposal(reason); return RejectSession(parm.Id, parm.Reason); @@ -139,7 +138,7 @@ public async Task EmitSessionEvent(string topic, EventData eventData, stri await this.SignClient.Emit(topic, eventData, chainId); } - public async Task DisconnectSession(string topic, ErrorResponse reason) + public async Task DisconnectSession(string topic, Error reason) { await this.SignClient.Disconnect(topic, reason); } diff --git a/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletApi.cs b/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletApi.cs index 67251c9..5f55586 100644 --- a/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletApi.cs +++ b/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletApi.cs @@ -3,7 +3,6 @@ using WalletConnectSharp.Network.Models; using WalletConnectSharp.Sign.Models; using WalletConnectSharp.Sign.Models.Engine.Events; -using ErrorResponse = WalletConnectSharp.Network.Models.ErrorResponse; namespace WalletConnectSharp.Web3Wallet.Interfaces; @@ -23,9 +22,9 @@ public interface IWeb3WalletApi Task ApproveSession(ProposalStruct proposal, params string[] approvedAddresses); - Task RejectSession(long id, ErrorResponse reason); + Task RejectSession(long id, Error reason); - Task RejectSession(ProposalStruct proposal, ErrorResponse reason); + Task RejectSession(ProposalStruct proposal, Error reason); Task RejectSession(ProposalStruct proposal, string reason); @@ -38,7 +37,7 @@ public interface IWeb3WalletApi Task EmitSessionEvent(string topic, EventData eventData, string chainId); - Task DisconnectSession(string topic, ErrorResponse reason); + Task DisconnectSession(string topic, Error reason); Task RespondAuthRequest(ResultResponse results, string iss); diff --git a/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletEngine.cs b/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletEngine.cs index 21a4c21..e80f517 100644 --- a/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletEngine.cs +++ b/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletEngine.cs @@ -4,7 +4,6 @@ using WalletConnectSharp.Network.Models; using WalletConnectSharp.Sign.Interfaces; using WalletConnectSharp.Sign.Models; -using ErrorResponse = WalletConnectSharp.Network.Models.ErrorResponse; namespace WalletConnectSharp.Web3Wallet.Interfaces; diff --git a/WalletConnectSharp.Web3Wallet/Web3Wallet.cs b/WalletConnectSharp.Web3Wallet/Web3WalletClient.cs similarity index 86% rename from WalletConnectSharp.Web3Wallet/Web3Wallet.cs rename to WalletConnectSharp.Web3Wallet/Web3WalletClient.cs index 40a16b4..16f9ccb 100644 --- a/WalletConnectSharp.Web3Wallet/Web3Wallet.cs +++ b/WalletConnectSharp.Web3Wallet/Web3WalletClient.cs @@ -7,11 +7,10 @@ using WalletConnectSharp.Sign.Models.Engine.Events; using WalletConnectSharp.Web3Wallet.Controllers; using WalletConnectSharp.Web3Wallet.Interfaces; -using ErrorResponse = WalletConnectSharp.Network.Models.ErrorResponse; namespace WalletConnectSharp.Web3Wallet; -public class Web3Wallet : IWeb3Wallet +public class Web3WalletClient : IWeb3Wallet { public string Name { get; } public string Context { get; } @@ -53,15 +52,15 @@ public IDictionary PendingAuthRequests public ICore Core { get; } public AuthMetadata Metadata { get; } - public static async Task Init(ICore core, AuthMetadata metadata, string name = null) + public static async Task Init(ICore core, AuthMetadata metadata, string name = null) { - var wallet = new Web3Wallet(core, metadata, name); + var wallet = new Web3WalletClient(core, metadata, name); await wallet.Initialize(); return wallet; } - private Web3Wallet(ICore core, AuthMetadata metadata, string name = null) + private Web3WalletClient(ICore core, AuthMetadata metadata, string name = null) { this.Metadata = metadata; this.Name = string.IsNullOrWhiteSpace(name) ? "Web3Wallet" : name; @@ -87,12 +86,12 @@ public Task ApproveSession(ProposalStruct proposal, params string return this.Engine.ApproveSession(proposal, approvedAddresses); } - public Task RejectSession(long id, ErrorResponse reason) + public Task RejectSession(long id, Error reason) { return this.Engine.RejectSession(id, reason); } - public Task RejectSession(ProposalStruct proposal, ErrorResponse reason) + public Task RejectSession(ProposalStruct proposal, Error reason) { return this.Engine.RejectSession(proposal, reason); } @@ -122,7 +121,7 @@ public Task EmitSessionEvent(string topic, EventData eventData, string cha return this.Engine.EmitSessionEvent(topic, eventData, topic); } - public Task DisconnectSession(string topic, ErrorResponse reason) + public Task DisconnectSession(string topic, Error reason) { return this.Engine.DisconnectSession(topic, reason); } diff --git a/WalletConnectSharpV2.sln b/WalletConnectSharpV2.sln index 2a0ff3c..35894ab 100644 --- a/WalletConnectSharpV2.sln +++ b/WalletConnectSharpV2.sln @@ -40,6 +40,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WalletConnectSharp.Auth.Tes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WalletConnectSharp.Web3Wallet", "WalletConnectSharp.Web3Wallet\WalletConnectSharp.Web3Wallet.csproj", "{53622C78-1162-46A2-9FFE-8E90B018A144}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WalletConnectSharp.Web3Wallet.Tests", "Tests\WalletConnectSharp.Web3Wallet.Tests\WalletConnectSharp.Web3Wallet.Tests.csproj", "{82C44097-B520-45A9-8228-FF35E6DDDC9D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -118,6 +120,10 @@ Global {53622C78-1162-46A2-9FFE-8E90B018A144}.Debug|Any CPU.Build.0 = Debug|Any CPU {53622C78-1162-46A2-9FFE-8E90B018A144}.Release|Any CPU.ActiveCfg = Release|Any CPU {53622C78-1162-46A2-9FFE-8E90B018A144}.Release|Any CPU.Build.0 = Release|Any CPU + {82C44097-B520-45A9-8228-FF35E6DDDC9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {82C44097-B520-45A9-8228-FF35E6DDDC9D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {82C44097-B520-45A9-8228-FF35E6DDDC9D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {82C44097-B520-45A9-8228-FF35E6DDDC9D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {23897558-4177-425D-B2FA-75AB0B6FEDAE} = {3A55D6C7-0EF8-4EEA-90A5-89F5AFB97BA0} @@ -134,5 +140,6 @@ Global {5C78A5E4-24ED-499C-91BC-90D16A626EFA} = {3A55D6C7-0EF8-4EEA-90A5-89F5AFB97BA0} {C3CC2BC9-CD29-46B8-94DB-B104E427330B} = {3A55D6C7-0EF8-4EEA-90A5-89F5AFB97BA0} {7B63C8BD-737F-403F-AF66-8A2E7ADBDBAB} = {3A55D6C7-0EF8-4EEA-90A5-89F5AFB97BA0} + {82C44097-B520-45A9-8228-FF35E6DDDC9D} = {3A55D6C7-0EF8-4EEA-90A5-89F5AFB97BA0} EndGlobalSection EndGlobal From 62b571eda37d49ae3c1d5981d01f07ff378a8dc4 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Mon, 12 Jun 2023 15:24:51 -0400 Subject: [PATCH 07/36] chore: started unit tests --- .../AuthClientTest.cs | 58 +- .../WalletConnectSharp.Auth.Tests.csproj | 1 - .../CryptoWalletFixture.cs | 40 ++ .../WalletConnectSharp.Tests.Common.csproj | 4 + .../AuthClientTest.cs | 524 ------------------ .../AuthTests.cs | 69 +++ .../Web3WalletFixture.cs | 10 +- .../WalletConnectAuthClient.cs | 3 +- 8 files changed, 157 insertions(+), 552 deletions(-) create mode 100644 Tests/WalletConnectSharp.Tests.Common/CryptoWalletFixture.cs delete mode 100644 Tests/WalletConnectSharp.Web3Wallet.Tests/AuthClientTest.cs create mode 100644 Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs diff --git a/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs b/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs index 0144fba..4c6ec8f 100644 --- a/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs +++ b/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs @@ -14,7 +14,7 @@ namespace WalletConnectSharp.Auth.Tests { - public class AuthClientTests : IClassFixture + public class AuthClientTests : IClassFixture, IClassFixture { private static readonly RequestParams DefaultRequestParams = new RequestParams() { @@ -24,9 +24,8 @@ public class AuthClientTests : IClassFixture Nonce = CryptoUtils.GenerateNonce() }; - private AuthClientFixture _authFixture; - private readonly string _iss; - private readonly Wallet _wallet; + private readonly AuthClientFixture _authFixture; + private readonly CryptoWalletFixture _cryptoWalletFixture; public IAuthClient PeerA { @@ -44,20 +43,35 @@ public IAuthClient PeerB } } + public string Iss + { + get + { + return _cryptoWalletFixture.Iss; + } + } + + public Wallet CryptoWallet + { + get + { + return _cryptoWalletFixture.CryptoWallet; + } + } + public string WalletAddress { get { - return _wallet.GetAddresses(1)[0]; + return _cryptoWalletFixture.WalletAddress; } } - public AuthClientTests(AuthClientFixture authFixture) + public AuthClientTests(AuthClientFixture authFixture, CryptoWalletFixture cryptoFixture) { this._authFixture = authFixture; - - this._wallet = new Wallet(Wordlist.English, WordCount.Twelve); - this._iss = $"did:pkh:eip155:1:{this.WalletAddress}"; + + this._cryptoWalletFixture = cryptoFixture; } [Fact, Trait("Category", "unit")] @@ -133,10 +147,10 @@ public async void TestKnownPairings() async void OnPeerBOnAuthRequested(object sender, AuthRequest request) { - var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this._iss); - var signature = await _wallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); + var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this.Iss); + var signature = await CryptoWallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); - await PeerB.Respond(new Cacao() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this._iss); + await PeerB.Respond(new Cacao() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this.Iss); Assert.Equal(Validation.Unknown, request.VerifyContext?.Validation); } @@ -226,7 +240,7 @@ public async void TestErrorResponses() async void OnPeerBOnAuthRequested(object sender, AuthRequest request) { - await PeerB.Respond(new ErrorResponse() { Error = new Network.Models.Error() { Code = 14001, Message = "Can not login" }, Id = request.Id }, this._iss); + await PeerB.Respond(new ErrorResponse() { Error = new Network.Models.Error() { Code = 14001, Message = "Can not login" }, Id = request.Id }, this.Iss); } void OnPeerAOnAuthResponded(object sender, AuthResponse response) { @@ -267,10 +281,10 @@ public async void HandlesSuccessfulResponse() async void OnPeerBOnAuthRequested(object sender, AuthRequest request) { - var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this._iss); - var signature = await _wallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); + var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this.Iss); + var signature = await CryptoWallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); - await PeerB.Respond(new ResultResponse() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this._iss); + await PeerB.Respond(new ResultResponse() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this.Iss); Assert.Equal(Validation.Unknown, request.VerifyContext?.Validation); } @@ -329,10 +343,10 @@ await Task.WhenAll(resolve1.Task, Task.Run(async () => async void OnPeerBOnAuthRequested(object sender, AuthRequest request) { - var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this._iss); - var signature = await _wallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); + var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this.Iss); + var signature = await CryptoWallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); - await PeerB.Respond(new ResultResponse() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this._iss); + await PeerB.Respond(new ResultResponse() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this.Iss); resolve3.SetResult(true); } @@ -495,10 +509,10 @@ public async void TestReceivesMetadata() async void OnPeerBOnAuthRequested(object sender, AuthRequest request) { receivedMetadataName = request.Parameters.Requester?.Metadata?.Name; - var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this._iss); - var signature = await _wallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); + var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this.Iss); + var signature = await CryptoWallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); - await PeerB.Respond(new ResultResponse() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this._iss); + await PeerB.Respond(new ResultResponse() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this.Iss); hasResponded.SetResult(true); Assert.Equal(Validation.Unknown, request.VerifyContext.Validation); diff --git a/Tests/WalletConnectSharp.Auth.Tests/WalletConnectSharp.Auth.Tests.csproj b/Tests/WalletConnectSharp.Auth.Tests/WalletConnectSharp.Auth.Tests.csproj index f181227..782a000 100644 --- a/Tests/WalletConnectSharp.Auth.Tests/WalletConnectSharp.Auth.Tests.csproj +++ b/Tests/WalletConnectSharp.Auth.Tests/WalletConnectSharp.Auth.Tests.csproj @@ -9,7 +9,6 @@ - runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Tests/WalletConnectSharp.Tests.Common/CryptoWalletFixture.cs b/Tests/WalletConnectSharp.Tests.Common/CryptoWalletFixture.cs new file mode 100644 index 0000000..c60ae2a --- /dev/null +++ b/Tests/WalletConnectSharp.Tests.Common/CryptoWalletFixture.cs @@ -0,0 +1,40 @@ +using NBitcoin; +using Nethereum.HdWallet; + +namespace WalletConnectSharp.Auth.Tests; + +public class CryptoWalletFixture +{ + private readonly Wallet _wallet; + private readonly string _iss; + + public string WalletAddress + { + get + { + return _wallet.GetAddresses(0)[0]; + } + } + + public Wallet CryptoWallet + { + get + { + return _wallet; + } + } + + public string Iss + { + get + { + return _iss; + } + } + + public CryptoWalletFixture() + { + this._wallet = new Wallet(Wordlist.English, WordCount.Twelve); + this._iss = $"did:pkh:eip155:1:{this.WalletAddress}"; + } +} diff --git a/Tests/WalletConnectSharp.Tests.Common/WalletConnectSharp.Tests.Common.csproj b/Tests/WalletConnectSharp.Tests.Common/WalletConnectSharp.Tests.Common.csproj index 2db8f9e..2663b15 100644 --- a/Tests/WalletConnectSharp.Tests.Common/WalletConnectSharp.Tests.Common.csproj +++ b/Tests/WalletConnectSharp.Tests.Common/WalletConnectSharp.Tests.Common.csproj @@ -13,4 +13,8 @@ + + + + diff --git a/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthClientTest.cs b/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthClientTest.cs deleted file mode 100644 index f84f19b..0000000 --- a/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthClientTest.cs +++ /dev/null @@ -1,524 +0,0 @@ -using System.Text; -using NBitcoin; -using Nethereum.HdWallet; -using WalletConnectSharp.Auth.Interfaces; -using WalletConnectSharp.Auth.Internals; -using WalletConnectSharp.Auth.Models; -using WalletConnectSharp.Core.Models.Pairing; -using WalletConnectSharp.Core.Models.Publisher; -using WalletConnectSharp.Core.Models.Relay; -using WalletConnectSharp.Core.Models.Verify; -using WalletConnectSharp.Events; -using Xunit; -using ErrorResponse = WalletConnectSharp.Auth.Models.ErrorResponse; - -namespace WalletConnectSharp.Auth.Tests -{ - public class AuthClientTests : IClassFixture - { - private static readonly RequestParams DefaultRequestParams = new RequestParams() - { - Aud = "http://localhost:3000/login", - Domain = "localhost:3000", - ChainId = "eip155:1", - Nonce = CryptoUtils.GenerateNonce() - }; - - private Web3WalletFixture _authFixture; - private readonly string _iss; - private readonly Wallet _wallet; - - public IAuthClient PeerA - { - get - { - return _authFixture.ClientA; - } - } - - public IAuthClient PeerB - { - get - { - return _authFixture.ClientB; - } - } - - public string WalletAddress - { - get - { - return _wallet.GetAddresses(1)[0]; - } - } - - public AuthClientTests(Web3WalletFixture authFixture) - { - this._authFixture = authFixture; - - this._wallet = new Wallet(Wordlist.English, WordCount.Twelve); - this._iss = $"did:pkh:eip155:1:{this.WalletAddress}"; - } - - [Fact, Trait("Category", "unit")] - public async void TestInit() - { - await _authFixture.WaitForClientsReady(); - - Assert.NotNull(PeerA); - Assert.NotNull(PeerB); - - Assert.NotNull(PeerA.Core); - Assert.NotNull(PeerA.Events); - Assert.NotNull(PeerA.Core.Expirer); - Assert.NotNull(PeerA.Core.History); - Assert.NotNull(PeerA.Core.Pairing); - - Assert.NotNull(PeerB.Core); - Assert.NotNull(PeerB.Events); - Assert.NotNull(PeerB.Core.Expirer); - Assert.NotNull(PeerB.Core.History); - Assert.NotNull(PeerB.Core.Pairing); - } - - [Fact, Trait("Category", "unit")] - public async void TestPairs() - { - await _authFixture.WaitForClientsReady(); - - var ogPairSize = PeerA.Core.Pairing.Pairings.Length; - - TaskCompletionSource authRequested = new TaskCompletionSource(); - - void OnPeerBOnAuthRequested(object o, AuthRequest authRequest) => authRequested.SetResult(true); - - PeerB.AuthRequested += OnPeerBOnAuthRequested; - - var uriData = await PeerA.Request(DefaultRequestParams); - var uri = uriData.Uri; - - await PeerB.Core.Pairing.Pair(uri); - - await authRequested.Task; - - Assert.Equal(PeerA.Core.Pairing.Pairings.Select(p => p.Key), PeerB.Core.Pairing.Pairings.Select(p => p.Key)); - Assert.Equal(ogPairSize + 1, PeerA.Core.Pairing.Pairings.Length); - - var peerAHistory = await PeerA.Core.History.JsonRpcHistoryOfType(); - var peerBHistory = await PeerB.Core.History.JsonRpcHistoryOfType(); - - Assert.Equal(peerAHistory.Size, peerBHistory.Size); - - Assert.True(PeerB.Core.Pairing.Pairings[0].Active); - - // Cleanup event listeners - PeerB.AuthRequested -= OnPeerBOnAuthRequested; - } - - [Fact, Trait("Category", "unit")] - public async void TestKnownPairings() - { - await _authFixture.WaitForClientsReady(); - - var ogSizeA = PeerA.Core.Pairing.Pairings.Length; - var history = await PeerA.AuthHistory(); - var ogHistorySizeA = history.Keys.Length; - - var ogSizeB = PeerB.Core.Pairing.Pairings.Length; - var historyB = await PeerB.AuthHistory(); - var ogHistorySizeB = historyB.Keys.Length; - - List responses = new List(); - TaskCompletionSource responseTask = new TaskCompletionSource(); - - async void OnPeerBOnAuthRequested(object sender, AuthRequest request) - { - var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this._iss); - var signature = await _wallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); - - await PeerB.Respond(new Cacao() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this._iss); - - Assert.Equal(Validation.Unknown, request.VerifyContext?.Validation); - } - - PeerB.AuthRequested += OnPeerBOnAuthRequested; - - void OnPeerAOnAuthResponded(object sender, AuthResponse args) - { - responses.Add(args); - responseTask.SetResult(args); - } - - PeerA.AuthResponded += OnPeerAOnAuthResponded; - - void OnPeerAOnAuthError(object sender, AuthErrorResponse args) - { - responses.Add(args); - responseTask.SetResult(args); - } - - PeerA.AuthError += OnPeerAOnAuthError; - - var requestData = await PeerA.Request(DefaultRequestParams); - - await PeerB.Core.Pairing.Pair(requestData.Uri); - - await responseTask.Task; - - // Reset - responseTask = new TaskCompletionSource(); - - // Get last pairing, that is the one we just made - var knownPairing = PeerA.Core.Pairing.Pairings[^1]; - - var requestData2 = await PeerA.Request(DefaultRequestParams, knownPairing.Topic); - - await responseTask.Task; - - Assert.Null(requestData2.Uri); - - Assert.Equal(ogSizeA + 1, PeerA.Core.Pairing.Pairings.Length); - Assert.Equal(ogHistorySizeA + 2, history.Keys.Length); - Assert.Equal(ogSizeB + 1, PeerB.Core.Pairing.Pairings.Length); - Assert.Equal(ogHistorySizeB + 2, historyB.Keys.Length); - Assert.Equal(responses[0].Topic, responses[1].Topic); - - // Cleanup event listeners - - PeerB.AuthRequested -= OnPeerBOnAuthRequested; - PeerA.AuthResponded -= OnPeerAOnAuthResponded; - PeerA.AuthError -= OnPeerAOnAuthError; - } - - [Fact, Trait("Category", "unit")] - public async void HandlesAuthRequests() - { - await _authFixture.WaitForClientsReady(); - - var ogSize = PeerB.Requests.Length; - - TaskCompletionSource receivedAuthRequest = new TaskCompletionSource(); - - void OnPeerBOnAuthRequested(object o, AuthRequest authRequest) => receivedAuthRequest.SetResult(true); - - PeerB.AuthRequested += OnPeerBOnAuthRequested; - - var requestData = await PeerA.Request(DefaultRequestParams); - - await PeerB.Core.Pairing.Pair(requestData.Uri); - - await receivedAuthRequest.Task; - - Assert.Equal(ogSize + 1, PeerB.Requests.Length); - - // Cleanup event listeners - PeerB.AuthRequested -= OnPeerBOnAuthRequested; - } - - [Fact, Trait("Category", "unit")] - public async void TestErrorResponses() - { - await _authFixture.WaitForClientsReady(); - - var ogPSize = PeerA.Core.Pairing.Pairings.Length; - - TaskCompletionSource errorResponse = new TaskCompletionSource(); - - async void OnPeerBOnAuthRequested(object sender, AuthRequest request) - { - await PeerB.Respond(new ErrorResponse() { Error = new Network.Models.Error() { Code = 14001, Message = "Can not login" }, Id = request.Id }, this._iss); - } - void OnPeerAOnAuthResponded(object sender, AuthResponse response) - { - errorResponse.SetResult(false); - } - - void OnPeerAOnAuthError(object sender, AuthErrorResponse response) => errorResponse.SetResult(true); - - PeerB.AuthRequested += OnPeerBOnAuthRequested; - PeerA.AuthResponded += OnPeerAOnAuthResponded; - PeerA.AuthError += OnPeerAOnAuthError; - - var requestData = await PeerA.Request(DefaultRequestParams); - - Assert.Equal(ogPSize + 1, PeerA.Core.Pairing.Pairings.Length); - Assert.False(PeerA.Core.Pairing.Pairings[^1].Active); - - await PeerB.Core.Pairing.Pair(requestData.Uri); - - await errorResponse.Task; - - Assert.False(PeerA.Core.Pairing.Pairings[^1].Active); - Assert.True(errorResponse.Task.Result); - - PeerB.AuthRequested -= OnPeerBOnAuthRequested; - PeerA.AuthResponded -= OnPeerAOnAuthResponded; - PeerA.AuthError -= OnPeerAOnAuthError; - } - - [Fact, Trait("Category", "unit")] - public async void HandlesSuccessfulResponse() - { - await _authFixture.WaitForClientsReady(); - - var ogPSize = PeerA.Core.Pairing.Pairings.Length; - - TaskCompletionSource successfulResponse = new TaskCompletionSource(); - - async void OnPeerBOnAuthRequested(object sender, AuthRequest request) - { - var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this._iss); - var signature = await _wallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); - - await PeerB.Respond(new ResultResponse() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this._iss); - - Assert.Equal(Validation.Unknown, request.VerifyContext?.Validation); - } - - PeerB.AuthRequested += OnPeerBOnAuthRequested; - - void OnPeerAOnAuthResponded(object sender, AuthResponse response) => successfulResponse.SetResult(response.Response.Result?.Signature != null); - - PeerA.AuthResponded += OnPeerAOnAuthResponded; - - void OnPeerAOnAuthError(object sender, AuthErrorResponse response) => successfulResponse.SetResult(false); - - PeerA.AuthError += OnPeerAOnAuthError; - - var requestData = await PeerA.Request(DefaultRequestParams); - - Assert.Equal(ogPSize + 1, PeerA.Core.Pairing.Pairings.Length); - Assert.False(PeerA.Core.Pairing.Pairings[^1].Active); - - await PeerB.Core.Pairing.Pair(requestData.Uri); - - await successfulResponse.Task; - - Assert.True(PeerA.Core.Pairing.Pairings[^1].Active); - Assert.True(successfulResponse.Task.Result); - - PeerB.AuthRequested -= OnPeerBOnAuthRequested; - PeerA.AuthResponded -= OnPeerAOnAuthResponded; - PeerA.AuthError -= OnPeerAOnAuthError; - } - - [Fact, Trait("Category", "unit")] - public async void TestCustomRequestExpiry() - { - await _authFixture.WaitForClientsReady(); - - var uri = ""; - var expiry = 1000; - - TaskCompletionSource resolve1 = new TaskCompletionSource(); - - PeerA.Core.Relayer.Once(RelayerEvents.Publish, (sender, @event) => - { - Assert.Equal(expiry, @event.EventData.Options?.TTL); - resolve1.SetResult(true); - }); - - - await Task.WhenAll(resolve1.Task, Task.Run(async () => - { - var response = await PeerA.Request(new RequestParams(DefaultRequestParams) { Expiry = expiry }); - uri = response.Uri; - })); - - TaskCompletionSource resolve3 = new TaskCompletionSource(); - - async void OnPeerBOnAuthRequested(object sender, AuthRequest request) - { - var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this._iss); - var signature = await _wallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); - - await PeerB.Respond(new ResultResponse() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this._iss); - resolve3.SetResult(true); - } - - PeerB.AuthRequested += OnPeerBOnAuthRequested; - - await Task.WhenAll(resolve3.Task, PeerB.Core.Pairing.Pair(uri)); - - PeerB.AuthRequested -= OnPeerBOnAuthRequested; - } - - [Fact, Trait("Category", "unit")] - public async void TestGetPendingPairings() - { - await _authFixture.WaitForClientsReady(); - - var ogCount = PeerB.PendingRequests.Count; - - TaskCompletionSource receivedAuthRequest = new TaskCompletionSource(); - var aud = "http://localhost:3000/login"; - - void OnPeerBOnAuthRequested(object sender, AuthRequest request) => receivedAuthRequest.SetResult(true); - - PeerB.AuthRequested += OnPeerBOnAuthRequested; - - var requestData = await PeerA.Request(DefaultRequestParams); - - await PeerB.Core.Pairing.Pair(requestData.Uri); - - await receivedAuthRequest.Task; - - var requests = PeerB.PendingRequests; - - Assert.Equal(ogCount + 1, requests.Count); - Assert.Contains(requests, r => r.Value.CacaoPayload.Aud == aud); - - PeerB.AuthRequested -= OnPeerBOnAuthRequested; - } - - [Fact, Trait("Category", "unit")] - public async void TestGetPairings() - { - var peerAOgSize = PeerA.Core.Pairing.Pairings.Length; - var peerBOgSize = PeerB.Core.Pairing.Pairings.Length; - - TaskCompletionSource receivedAuthRequest = new TaskCompletionSource(); - - void OnPeerBOnAuthRequested(object sender, AuthRequest request) => receivedAuthRequest.SetResult(true); - - PeerB.AuthRequested += OnPeerBOnAuthRequested; - - var requestData = await PeerA.Request(DefaultRequestParams); - - await PeerB.Core.Pairing.Pair(requestData.Uri); - - await receivedAuthRequest.Task; - - var clientPairings = PeerA.Core.Pairing.Pairings; - var peerPairings = PeerB.Core.Pairing.Pairings; - - Assert.Equal(peerAOgSize + 1, clientPairings.Length); - Assert.Equal(peerBOgSize + 1, peerPairings.Length); - Assert.Equal(clientPairings[^1].Topic, peerPairings[^1].Topic); - - PeerB.AuthRequested -= OnPeerBOnAuthRequested; - } - - [Fact, Trait("Category", "unit")] - public async void TestPing() - { - await _authFixture.WaitForClientsReady(); - - TaskCompletionSource receivedAuthRequest = new TaskCompletionSource(); - TaskCompletionSource receivedClientPing = new TaskCompletionSource(); - TaskCompletionSource receivedPeerPing = new TaskCompletionSource(); - - void OnPeerBOnAuthRequested(object sender, AuthRequest request) => receivedAuthRequest.SetResult(true); - - PeerB.AuthRequested += OnPeerBOnAuthRequested; - - PeerB.Core.Pairing.Once(PairingEvents.PairingPing, (sender, @event) => - { - receivedPeerPing.SetResult(true); - }); - - PeerA.Core.Pairing.Once(PairingEvents.PairingPing, (sender, @event) => - { - receivedClientPing.SetResult(true); - }); - - var requestData = await PeerA.Request(DefaultRequestParams); - - await PeerB.Core.Pairing.Pair(requestData.Uri); - - await receivedAuthRequest.Task; - - var pairing = PeerA.Core.Pairing.Pairings[^1]; - await PeerA.Core.Pairing.Ping(pairing.Topic); - await PeerB.Core.Pairing.Ping(pairing.Topic); - - await Task.WhenAll(receivedClientPing.Task, receivedPeerPing.Task); - - Assert.True(receivedClientPing.Task.Result); - Assert.True(receivedPeerPing.Task.Result); - - PeerB.AuthRequested -= OnPeerBOnAuthRequested; - } - - [Fact, Trait("Category", "unit")] - public async void TestDisconnectedPairing() - { - await _authFixture.WaitForClientsReady(); - - var peerAOgSize = PeerA.Core.Pairing.Pairings.Length; - var peerBOgSize = PeerB.Core.Pairing.Pairings.Length; - - TaskCompletionSource receivedAuthRequest = new TaskCompletionSource(); - TaskCompletionSource peerDeletedPairing = new TaskCompletionSource(); - - void OnPeerBOnAuthRequested(object sender, AuthRequest request) => receivedAuthRequest.SetResult(true); - - PeerB.AuthRequested += OnPeerBOnAuthRequested; - - PeerB.Core.Pairing.Once(PairingEvents.PairingDelete, (sender, @event) => - { - peerDeletedPairing.SetResult(true); - }); - - var requestData = await PeerA.Request(DefaultRequestParams); - - await PeerB.Core.Pairing.Pair(requestData.Uri); - - await receivedAuthRequest.Task; - - var clientPairings = PeerA.Core.Pairing.Pairings; - var peerPairings = PeerB.Core.Pairing.Pairings; - - Assert.Equal(peerAOgSize + 1, PeerA.Core.Pairing.Pairings.Length); - Assert.Equal(peerBOgSize + 1, PeerB.Core.Pairing.Pairings.Length); - Assert.Equal(clientPairings[^1].Topic, peerPairings[^1].Topic); - - await PeerA.Core.Pairing.Disconnect(clientPairings[^1].Topic); - - await peerDeletedPairing.Task; - Assert.Equal(peerAOgSize, PeerA.Core.Pairing.Pairings.Length); - Assert.Equal(peerBOgSize, PeerB.Core.Pairing.Pairings.Length); - - PeerB.AuthRequested -= OnPeerBOnAuthRequested; - } - - [Fact, Trait("Category", "unit")] - public async void TestReceivesMetadata() - { - await _authFixture.WaitForClientsReady(); - - var receivedMetadataName = ""; - var ogPairingSize = PeerA.Core.Pairing.Pairings.Length; - - TaskCompletionSource hasResponded = new TaskCompletionSource(); - - async void OnPeerBOnAuthRequested(object sender, AuthRequest request) - { - receivedMetadataName = request.Parameters.Requester?.Metadata?.Name; - var message = PeerB.FormatMessage(request.Parameters.CacaoPayload, this._iss); - var signature = await _wallet.GetAccount(WalletAddress).AccountSigningService.PersonalSign.SendRequestAsync(Encoding.UTF8.GetBytes(message)); - - await PeerB.Respond(new ResultResponse() { Id = request.Id, Signature = new Cacao.CacaoSignature.EIP191CacaoSignature(signature) }, this._iss); - - hasResponded.SetResult(true); - Assert.Equal(Validation.Unknown, request.VerifyContext.Validation); - } - - PeerB.AuthRequested += OnPeerBOnAuthRequested; - - var requestData = await PeerA.Request(DefaultRequestParams); - - Assert.Equal(ogPairingSize + 1, PeerA.Core.Pairing.Pairings.Length); - Assert.False(PeerA.Core.Pairing.Pairings[^1].Active); - - await PeerB.Core.Pairing.Pair(requestData.Uri); - - await hasResponded.Task; - - Assert.True(PeerA.Core.Pairing.Pairings[^1].Active); - Assert.True(hasResponded.Task.Result); - Assert.Equal(PeerA.Metadata.Name, receivedMetadataName); - PeerB.AuthRequested -= OnPeerBOnAuthRequested; - } - } -} diff --git a/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs b/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs new file mode 100644 index 0000000..a824c09 --- /dev/null +++ b/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs @@ -0,0 +1,69 @@ +using WalletConnectSharp.Auth.Internals; +using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Core; +using WalletConnectSharp.Core.Models; +using WalletConnectSharp.Tests.Common; +using WalletConnectSharp.Web3Wallet; +using Xunit; + +namespace WalletConnectSharp.Auth.Tests +{ + public class AuthClientTests : IClassFixture, IAsyncLifetime + { + private static readonly RequestParams DefaultRequestParams = new RequestParams() + { + Aud = "http://localhost:3000/login", + Domain = "localhost:3000", + ChainId = "eip155:1", + Nonce = CryptoUtils.GenerateNonce() + }; + + private readonly CryptoWalletFixture _cryptoWalletFixture; + private WalletConnectCore _core; + private WalletConnectAuthClient _dapp; + private Web3WalletClient _wallet; + + + public string WalletAddress + { + get + { + return _cryptoWalletFixture.WalletAddress; + } + } + + public AuthClientTests(CryptoWalletFixture cryptoWalletFixture) + { + this._cryptoWalletFixture = cryptoWalletFixture; + } + + public async Task InitializeAsync() + { + _core = new WalletConnectCore(new CoreOptions() + { + ProjectId = TestValues.TestProjectId, RelayUrl = TestValues.TestRelayUrl, + }); + _dapp = await WalletConnectAuthClient.Init(new AuthOptions() + { + ProjectId = TestValues.TestProjectId, + Metadata = new AuthMetadata(), + Name = "dapp", + }); + _wallet = await Web3WalletClient.Init(_core, new AuthMetadata(), "wallet"); + + Assert.NotNull(_wallet); + Assert.NotNull(_dapp); + Assert.NotNull(_core); + Assert.Null(_wallet.Metadata.Redirect); + Assert.Null(_dapp.Metadata.Redirect); + } + + public Task DisposeAsync() + { + if (_core.Relayer.Connected) + { + _core.Relayer. + } + } + } +} diff --git a/Tests/WalletConnectSharp.Web3Wallet.Tests/Web3WalletFixture.cs b/Tests/WalletConnectSharp.Web3Wallet.Tests/Web3WalletFixture.cs index 96c4b53..54ce2f0 100644 --- a/Tests/WalletConnectSharp.Web3Wallet.Tests/Web3WalletFixture.cs +++ b/Tests/WalletConnectSharp.Web3Wallet.Tests/Web3WalletFixture.cs @@ -1,4 +1,5 @@ -using WalletConnectSharp.Core.Interfaces; +using WalletConnectSharp.Core; +using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Core.Models; using WalletConnectSharp.Storage; using WalletConnectSharp.Tests.Common; @@ -55,9 +56,10 @@ protected override async void Init() Url = "https://walletconnect.com" }; - CoreA = new Core.Core(OptionsA); + CoreA = new WalletConnectCore(OptionsA); + CoreB = new WalletConnectCore(OptionsB); - ClientA = await Web3WalletClient.Init(OptionsA); - ClientB = await Web3WalletClient.Init(OptionsB); + ClientA = await Web3WalletClient.Init(CoreA, MetadataA); + ClientB = await Web3WalletClient.Init(CoreB, MetadataB); } } diff --git a/WalletConnectSharp.Auth/WalletConnectAuthClient.cs b/WalletConnectSharp.Auth/WalletConnectAuthClient.cs index 1d43643..40572c5 100644 --- a/WalletConnectSharp.Auth/WalletConnectAuthClient.cs +++ b/WalletConnectSharp.Auth/WalletConnectAuthClient.cs @@ -1,6 +1,7 @@ using WalletConnectSharp.Auth.Controllers; using WalletConnectSharp.Auth.Interfaces; using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Core; using WalletConnectSharp.Core.Controllers; using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Events; @@ -99,7 +100,7 @@ private WalletConnectAuthClient(AuthOptions options) if (string.IsNullOrWhiteSpace(options.Name)) options.Name = $"{options.Metadata.Name}-{Name}"; - Core = options.Core ?? new Core.Core(options); + Core = options.Core ?? new WalletConnectCore(options); AuthKeys = new Store(Core, "authKeys", AUTH_CLIENT_STORAGE_PREFIX); PairingTopics = new Store(Core, "pairingTopics", AUTH_CLIENT_STORAGE_PREFIX); From 12d04ad9e5df41e85d4fbae327ca4c916d797404 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Wed, 14 Jun 2023 23:37:53 -0400 Subject: [PATCH 08/36] fix: compile errors caused by merge --- Directory.Packages.props | 1 + .../WalletConnectSharp.Tests.Common.csproj | 2 +- Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs | 4 ++-- WalletConnectSharp.Core/Models/Relay/RelayerEvents.cs | 2 -- WalletConnectSharp.Core/WalletConnectCore.cs | 4 ++++ 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index e539bca..5f69520 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,6 +5,7 @@ + diff --git a/Tests/WalletConnectSharp.Tests.Common/WalletConnectSharp.Tests.Common.csproj b/Tests/WalletConnectSharp.Tests.Common/WalletConnectSharp.Tests.Common.csproj index 2663b15..eee131d 100644 --- a/Tests/WalletConnectSharp.Tests.Common/WalletConnectSharp.Tests.Common.csproj +++ b/Tests/WalletConnectSharp.Tests.Common/WalletConnectSharp.Tests.Common.csproj @@ -14,7 +14,7 @@ - + diff --git a/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs b/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs index a824c09..499f6ba 100644 --- a/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs +++ b/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs @@ -58,11 +58,11 @@ public async Task InitializeAsync() Assert.Null(_dapp.Metadata.Redirect); } - public Task DisposeAsync() + public async Task DisposeAsync() { if (_core.Relayer.Connected) { - _core.Relayer. + await _core.Relayer.TransportClose(); } } } diff --git a/WalletConnectSharp.Core/Models/Relay/RelayerEvents.cs b/WalletConnectSharp.Core/Models/Relay/RelayerEvents.cs index ec942de..fedd92d 100644 --- a/WalletConnectSharp.Core/Models/Relay/RelayerEvents.cs +++ b/WalletConnectSharp.Core/Models/Relay/RelayerEvents.cs @@ -9,8 +9,6 @@ public static class RelayerEvents public static readonly string ConnectionStalled = "relayer_connection_stalled"; - public static readonly string Publish = "relayer_publish"; - /// /// The event id for the publish event /// diff --git a/WalletConnectSharp.Core/WalletConnectCore.cs b/WalletConnectSharp.Core/WalletConnectCore.cs index 56d4670..02f75a2 100644 --- a/WalletConnectSharp.Core/WalletConnectCore.cs +++ b/WalletConnectSharp.Core/WalletConnectCore.cs @@ -2,6 +2,7 @@ using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Core.Models; using WalletConnectSharp.Core.Models.Relay; +using WalletConnectSharp.Core.Models.Verify; using WalletConnectSharp.Crypto; using WalletConnectSharp.Crypto.Interfaces; using WalletConnectSharp.Events; @@ -109,6 +110,8 @@ public string Context /// public IPairing Pairing { get; } + public Verifier Verify { get; } + /// /// Create a new Core with the given options. /// @@ -146,6 +149,7 @@ public WalletConnectCore(CoreOptions options = null) Events = new EventDelegator(this); Expirer = new Expirer(this); Pairing = new Pairing(this); + Verify = new Verifier(); Relayer = new Relayer(new RelayerOptions() { From 3bdfc8e9bde9f8dc978be6a0a11ee1bff9028708 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Thu, 15 Jun 2023 16:02:33 -0400 Subject: [PATCH 09/36] feat: complete auth tests for web3wallet --- .../AuthClientTest.cs | 2 +- .../CryptoWalletFixture.cs | 65 +++++--- .../AuthTests.cs | 149 +++++++++++++++++- .../Web3WalletFixture.cs | 65 -------- .../Interfaces/IAuthClient.cs | 9 +- .../Interfaces/IAuthClientEvents.cs | 10 ++ .../Interfaces/IPendingRequests.cs | 9 +- .../Controllers/Web3WalletEngine.cs | 49 +++++- .../Interfaces/IWeb3WalletApi.cs | 7 +- .../Web3WalletClient.cs | 44 ++++++ 10 files changed, 301 insertions(+), 108 deletions(-) delete mode 100644 Tests/WalletConnectSharp.Web3Wallet.Tests/Web3WalletFixture.cs create mode 100644 WalletConnectSharp.Auth/Interfaces/IAuthClientEvents.cs diff --git a/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs b/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs index 4c6ec8f..8bd8bbf 100644 --- a/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs +++ b/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs @@ -1,5 +1,4 @@ using System.Text; -using NBitcoin; using Nethereum.HdWallet; using WalletConnectSharp.Auth.Interfaces; using WalletConnectSharp.Auth.Internals; @@ -9,6 +8,7 @@ using WalletConnectSharp.Core.Models.Relay; using WalletConnectSharp.Core.Models.Verify; using WalletConnectSharp.Events; +using WalletConnectSharp.Tests.Common; using Xunit; using ErrorResponse = WalletConnectSharp.Auth.Models.ErrorResponse; diff --git a/Tests/WalletConnectSharp.Tests.Common/CryptoWalletFixture.cs b/Tests/WalletConnectSharp.Tests.Common/CryptoWalletFixture.cs index c60ae2a..12f24d4 100644 --- a/Tests/WalletConnectSharp.Tests.Common/CryptoWalletFixture.cs +++ b/Tests/WalletConnectSharp.Tests.Common/CryptoWalletFixture.cs @@ -1,40 +1,53 @@ -using NBitcoin; +using System.Text; +using NBitcoin; using Nethereum.HdWallet; -namespace WalletConnectSharp.Auth.Tests; - -public class CryptoWalletFixture +namespace WalletConnectSharp.Tests.Common { - private readonly Wallet _wallet; - private readonly string _iss; - - public string WalletAddress + public class CryptoWalletFixture { - get + private readonly Wallet _wallet; + private readonly string _iss; + + public string WalletAddress { - return _wallet.GetAddresses(0)[0]; + get + { + return _wallet.GetAddresses(0)[0]; + } } - } - public Wallet CryptoWallet - { - get + public Wallet CryptoWallet { - return _wallet; + get + { + return _wallet; + } } - } - public string Iss - { - get + public string Iss { - return _iss; + get + { + return _iss; + } + } + + public CryptoWalletFixture() + { + this._wallet = new Wallet(Wordlist.English, WordCount.Twelve); + this._iss = $"did:pkh:eip155:1:{this.WalletAddress}"; + } + + public Task SignMessage(string message) + { + return _wallet + .GetAccount(WalletAddress) + .AccountSigningService + .PersonalSign + .SendRequestAsync( + Encoding.UTF8.GetBytes(message) + ); } - } - - public CryptoWalletFixture() - { - this._wallet = new Wallet(Wordlist.English, WordCount.Twelve); - this._iss = $"did:pkh:eip155:1:{this.WalletAddress}"; } } diff --git a/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs b/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs index 499f6ba..c135f3e 100644 --- a/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs +++ b/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs @@ -1,12 +1,13 @@ +using WalletConnectSharp.Auth; using WalletConnectSharp.Auth.Internals; using WalletConnectSharp.Auth.Models; using WalletConnectSharp.Core; using WalletConnectSharp.Core.Models; +using WalletConnectSharp.Network.Models; using WalletConnectSharp.Tests.Common; -using WalletConnectSharp.Web3Wallet; using Xunit; -namespace WalletConnectSharp.Auth.Tests +namespace WalletConnectSharp.Web3Wallet.Tests { public class AuthClientTests : IClassFixture, IAsyncLifetime { @@ -22,8 +23,8 @@ public class AuthClientTests : IClassFixture, IAsyncLifetim private WalletConnectCore _core; private WalletConnectAuthClient _dapp; private Web3WalletClient _wallet; + private string uriString; - public string WalletAddress { get @@ -32,6 +33,14 @@ public string WalletAddress } } + public string Iss + { + get + { + return _cryptoWalletFixture.Iss; + } + } + public AuthClientTests(CryptoWalletFixture cryptoWalletFixture) { this._cryptoWalletFixture = cryptoWalletFixture; @@ -65,5 +74,139 @@ public async Task DisposeAsync() await _core.Relayer.TransportClose(); } } + + [Fact, Trait("Category", "unit")] + public async void TestRespondToAuthRequest() + { + var request = await _dapp.Request(DefaultRequestParams); + uriString = request.Uri; + + TaskCompletionSource task1 = new TaskCompletionSource(); + _wallet.AuthRequested += async (sender, authRequest) => + { + Assert.Equal(DefaultRequestParams.Aud, authRequest.Parameters.CacaoPayload.Aud); + Assert.Equal(DefaultRequestParams.Domain, authRequest.Parameters.CacaoPayload.Domain); + Assert.Equal(DefaultRequestParams.Nonce, authRequest.Parameters.CacaoPayload.Nonce); + + var message = _wallet.FormatMessage(authRequest.Parameters.CacaoPayload, Iss); + var signature = await _cryptoWalletFixture.SignMessage(message); + + await _wallet.RespondAuthRequest(authRequest, signature, Iss); + + task1.TrySetResult(true); + }; + + TaskCompletionSource task2 = new TaskCompletionSource(); + _dapp.AuthResponded += (sender, response) => + { + Assert.NotNull(response); + Assert.NotNull(response.Id); + Assert.NotNull(response.Topic); + Assert.NotNull(response.Topic); + Assert.Equal(Iss, response.Response.Result.Payload.Iss); + task2.TrySetResult(true); + }; + + _dapp.AuthError += (sender, response) => task1.TrySetException(response.Error.ToException()); + + await Task.WhenAll( + task1.Task, + task2.Task, + _wallet.Pair(uriString, true) + ); + } + + [Fact, Trait("Category", "unit")] + public async void TestShouldRejectAuthRequest() + { + var request = await _dapp.Request(DefaultRequestParams); + uriString = request.Uri; + var errorResponse = new Error() + { + Code = 14001, + Message = "Can not login" + }; + + TaskCompletionSource task1 = new TaskCompletionSource(); + + _wallet.AuthRequested += (sender, authRequest) => + { + Assert.Equal(DefaultRequestParams.Aud, authRequest.Parameters.CacaoPayload.Aud); + Assert.Equal(DefaultRequestParams.Domain, authRequest.Parameters.CacaoPayload.Domain); + Assert.Equal(DefaultRequestParams.Nonce, authRequest.Parameters.CacaoPayload.Nonce); + + _wallet.RespondAuthRequest(authRequest, errorResponse, Iss); + + task1.TrySetResult(true); + }; + + TaskCompletionSource task2 = new TaskCompletionSource(); + _dapp.AuthResponded += (sender, response) => + { + task2.SetException(new Exception("Did not get an error response")); + }; + + _dapp.AuthError += (sender, response) => + { + Assert.NotNull(response); + Assert.NotNull(response.Id); + Assert.NotNull(response.Topic); + Assert.Equal(errorResponse, response.Error); + + task2.TrySetResult(true); + }; + + await Task.WhenAll( + task1.Task, + task2.Task, + _wallet.Pair(uriString, true) + ); + } + + [Fact, Trait("Category", "unit")] + public async void TestGetPendingAuthRequest() + { + var request = await _dapp.Request(DefaultRequestParams); + uriString = request.Uri; + + TaskCompletionSource task1 = new TaskCompletionSource(); + _wallet.AuthRequested += async (sender, authRequest) => + { + Assert.NotNull(authRequest.Id); + + var pendingRequest = _wallet.PendingAuthRequests[(long)authRequest.Id]; + + + Assert.Equal(DefaultRequestParams.Aud, pendingRequest.CacaoPayload.Aud); + Assert.Equal(DefaultRequestParams.Domain, pendingRequest.CacaoPayload.Domain); + Assert.Equal(DefaultRequestParams.Nonce, pendingRequest.CacaoPayload.Nonce); + + var message = _wallet.FormatMessage(pendingRequest.CacaoPayload, Iss); + var signature = await _cryptoWalletFixture.SignMessage(message); + + await _wallet.RespondAuthRequest(authRequest, signature, Iss); + + task1.TrySetResult(true); + }; + + TaskCompletionSource task2 = new TaskCompletionSource(); + _dapp.AuthResponded += (sender, response) => + { + Assert.NotNull(response); + Assert.NotNull(response.Id); + Assert.NotNull(response.Topic); + Assert.NotNull(response.Topic); + Assert.Equal(Iss, response.Response.Result.Payload.Iss); + task2.TrySetResult(true); + }; + + _dapp.AuthError += (sender, response) => task1.TrySetException(response.Error.ToException()); + + await Task.WhenAll( + task1.Task, + task2.Task, + _wallet.Pair(uriString, true) + ); + } } } diff --git a/Tests/WalletConnectSharp.Web3Wallet.Tests/Web3WalletFixture.cs b/Tests/WalletConnectSharp.Web3Wallet.Tests/Web3WalletFixture.cs deleted file mode 100644 index 54ce2f0..0000000 --- a/Tests/WalletConnectSharp.Web3Wallet.Tests/Web3WalletFixture.cs +++ /dev/null @@ -1,65 +0,0 @@ -using WalletConnectSharp.Core; -using WalletConnectSharp.Core.Interfaces; -using WalletConnectSharp.Core.Models; -using WalletConnectSharp.Storage; -using WalletConnectSharp.Tests.Common; -using WalletConnectSharp.Web3Wallet.Interfaces; -using WalletConnectSharp.Web3Wallet; - -namespace WalletConnectSharp.Auth.Tests; - -public class Web3WalletFixture : TwoClientsFixture -{ - public CoreOptions OptionsA { get; protected set; } - - public CoreOptions OptionsB { get; protected set; } - - public AuthMetadata MetadataA { get; protected set; } - - public AuthMetadata MetadataB { get; protected set; } - - public ICore CoreA { get; protected set; } - - public ICore CoreB { get; protected set; } - - protected override async void Init() - { - MetadataA = new AuthMetadata() - { - Description = "An example dapp to showcase WalletConnectSharpv2", - Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, - Name = "WalletConnectSharpv2 Dapp Example", - Url = "https://walletconnect.com" - }; - - OptionsA = new CoreOptions() - { - ProjectId = TestValues.TestProjectId, - RelayUrl = TestValues.TestRelayUrl, - // Omit if you want persistant storage - Storage = new InMemoryStorage(), - }; - - OptionsB = new CoreOptions() - { - ProjectId = TestValues.TestProjectId, - RelayUrl = TestValues.TestRelayUrl, - // Omit if you want persistant storage - Storage = new InMemoryStorage() - }; - - MetadataB = new AuthMetadata() - { - Description = "An example wallet to showcase WalletConnectSharpv2", - Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, - Name = "WalletConnectSharpv2 Wallet Example", - Url = "https://walletconnect.com" - }; - - CoreA = new WalletConnectCore(OptionsA); - CoreB = new WalletConnectCore(OptionsB); - - ClientA = await Web3WalletClient.Init(CoreA, MetadataA); - ClientB = await Web3WalletClient.Init(CoreB, MetadataB); - } -} diff --git a/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs b/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs index b73b192..565018b 100644 --- a/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs +++ b/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs @@ -5,15 +5,11 @@ namespace WalletConnectSharp.Auth.Interfaces; -public interface IAuthClient : IModule, IEvents +public interface IAuthClient : IModule, IEvents, IAuthClientEvents { string Protocol { get; } int Version { get; } - - event EventHandler AuthRequested; - event EventHandler AuthResponded; - event EventHandler AuthError; - + ICore Core { get; set; } AuthMetadata Metadata { get; set; } string ProjectId { get; set; } @@ -34,7 +30,6 @@ public interface IAuthClient : IModule, IEvents Task> AuthHistory(); string FormatMessage(Cacao.CacaoPayload cacao); - string FormatMessage(Cacao.CacaoRequestPayload cacao, string iss); diff --git a/WalletConnectSharp.Auth/Interfaces/IAuthClientEvents.cs b/WalletConnectSharp.Auth/Interfaces/IAuthClientEvents.cs new file mode 100644 index 0000000..fba7210 --- /dev/null +++ b/WalletConnectSharp.Auth/Interfaces/IAuthClientEvents.cs @@ -0,0 +1,10 @@ +using WalletConnectSharp.Auth.Models; + +namespace WalletConnectSharp.Auth.Interfaces; + +public interface IAuthClientEvents +{ + event EventHandler AuthRequested; + event EventHandler AuthResponded; + event EventHandler AuthError; +} diff --git a/WalletConnectSharp.Sign/Interfaces/IPendingRequests.cs b/WalletConnectSharp.Sign/Interfaces/IPendingRequests.cs index a526e45..abecf65 100644 --- a/WalletConnectSharp.Sign/Interfaces/IPendingRequests.cs +++ b/WalletConnectSharp.Sign/Interfaces/IPendingRequests.cs @@ -1,6 +1,9 @@ using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Sign.Models; -namespace WalletConnectSharp.Sign.Interfaces; - -public interface IPendingRequests : IStore { } +namespace WalletConnectSharp.Sign.Interfaces +{ + public interface IPendingRequests : IStore + { + } +} diff --git a/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs b/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs index 6ded19a..2a1ba44 100644 --- a/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs +++ b/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs @@ -153,6 +153,19 @@ public async Task RespondAuthRequest(AuthErrorResponse error, string iss) await this.AuthClient.Respond(error, iss); } + public Task RespondAuthRequest(AuthRequest request, Error error, string iss) + { + return RespondAuthRequest(new AuthErrorResponse() { Id = request.Id, Error = error, }, iss); + } + + public Task RespondAuthRequest(AuthRequest request, string signature, string iss, bool eip191 = true) + { + Cacao.CacaoSignature sig = eip191 + ? new Cacao.CacaoSignature.EIP191CacaoSignature(signature) + : new Cacao.CacaoSignature.EIP1271CacaoSignature(signature); + return RespondAuthRequest(new ResultResponse() { Id = request.Id, Signature = sig }, iss); + } + public string FormatMessage(Cacao.CacaoRequestPayload payload, string iss) { return this.AuthClient.FormatMessage(payload, iss); @@ -162,7 +175,7 @@ private void PropagateEventToClient(string eventName, IEvents source) { EventHandler> eventHandler = (sender, @event) => { - this.Client.Events.Trigger(eventName, @event); + this.Client.Events.TriggerType(eventName, @event, @event.GetType()); }; source.On(eventName, eventHandler); @@ -173,7 +186,11 @@ private void InitializeEventListeners() PropagateEventToClient("session_proposal", SignClient); PropagateEventToClient("session_request", SignClient); PropagateEventToClient("session_delete", SignClient); - PropagateEventToClient("auth_request", AuthClient); + + // Propagate auth events + AuthClient.AuthRequested += OnAuthRequest; + AuthClient.AuthResponded += OnAuthResponse; + AuthClient.AuthError += OnAuthResponse; } private void IsInitialized() @@ -183,4 +200,32 @@ private void IsInitialized() throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, "Web3WalletEngine"); } } + + public event EventHandler AuthRequested; + public event EventHandler AuthResponded; + public event EventHandler AuthError; + + void OnAuthRequest(object sender, AuthRequest request) + { + if (AuthRequested != null) + { + AuthRequested(sender, request); + } + } + + void OnAuthResponse(object sender, AuthErrorResponse errorResponse) + { + if (AuthError != null) + { + AuthError(sender, errorResponse); + } + } + + void OnAuthResponse(object sender, AuthResponse response) + { + if (AuthResponded != null) + { + AuthResponded(sender, response); + } + } } diff --git a/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletApi.cs b/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletApi.cs index 5f55586..f0a796a 100644 --- a/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletApi.cs +++ b/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3WalletApi.cs @@ -1,4 +1,5 @@ using WalletConnectSharp.Auth; +using WalletConnectSharp.Auth.Interfaces; using WalletConnectSharp.Auth.Models; using WalletConnectSharp.Network.Models; using WalletConnectSharp.Sign.Models; @@ -6,7 +7,7 @@ namespace WalletConnectSharp.Web3Wallet.Interfaces; -public interface IWeb3WalletApi +public interface IWeb3WalletApi : IAuthClientEvents { IDictionary ActiveSessions { get; } @@ -42,6 +43,10 @@ public interface IWeb3WalletApi Task RespondAuthRequest(ResultResponse results, string iss); Task RespondAuthRequest(AuthErrorResponse error, string iss); + + Task RespondAuthRequest(AuthRequest request, Error error, string iss); + + Task RespondAuthRequest(AuthRequest request, string signature, string iss, bool eip191 = true); string FormatMessage(Cacao.CacaoRequestPayload payload, string iss); } diff --git a/WalletConnectSharp.Web3Wallet/Web3WalletClient.cs b/WalletConnectSharp.Web3Wallet/Web3WalletClient.cs index 16f9ccb..6aa82bf 100644 --- a/WalletConnectSharp.Web3Wallet/Web3WalletClient.cs +++ b/WalletConnectSharp.Web3Wallet/Web3WalletClient.cs @@ -136,6 +136,16 @@ public Task RespondAuthRequest(AuthErrorResponse error, string iss) return this.Engine.RespondAuthRequest(error, iss); } + public Task RespondAuthRequest(AuthRequest request, Error error, string iss) + { + return this.Engine.RespondAuthRequest(request, error, iss); + } + + public Task RespondAuthRequest(AuthRequest request, string signature, string iss, bool eip191 = true) + { + return this.Engine.RespondAuthRequest(request, signature, iss, eip191); + } + public string FormatMessage(Cacao.CacaoRequestPayload payload, string iss) { return this.Engine.FormatMessage(payload, iss); @@ -145,4 +155,38 @@ private Task Initialize() { return this.Engine.Init(); } + + public event EventHandler AuthRequested + { + add + { + Engine.AuthRequested += value; + } + remove + { + Engine.AuthRequested -= value; + } + } + public event EventHandler AuthResponded + { + add + { + Engine.AuthResponded += value; + } + remove + { + Engine.AuthResponded -= value; + } + } + public event EventHandler AuthError + { + add + { + Engine.AuthError += value; + } + remove + { + Engine.AuthError -= value; + } + } } From 3f4b7a39f14f6d814bf6aa1ed8e61685c0938698 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Thu, 15 Jun 2023 18:09:32 -0400 Subject: [PATCH 10/36] feat: complete sign tests for web3wallet --- .../AuthClientFixture.cs | 5 +- .../AuthTests.cs | 4 +- .../SignTests.cs | 668 ++++++++++++++++++ WalletConnectSharp.Auth/AuthMetadata.cs | 14 - .../Controllers/AuthEngine.cs | 3 +- .../Interfaces/IAuthClient.cs | 3 +- WalletConnectSharp.Auth/Models/AuthOptions.cs | 5 +- .../Models/RedirectData.cs | 12 - WalletConnectSharp.Auth/Models/Requester.cs | 3 +- .../WalletConnectAuthClient.cs | 2 +- .../Controllers/TypedMessageHandler.cs | 2 +- WalletConnectSharp.Core/Metadata.cs | 7 + .../Models/MessageHandler/RequestEventArgs.cs | 8 +- .../MessageHandler/TypedEventHandler.cs | 40 +- .../Models/RedirectData.cs | 11 + WalletConnectSharp.Sign/Engine.cs | 16 +- .../Interfaces/IEngineAPI.cs | 4 + .../Internals/EngineHandler.cs | 10 +- .../Internals/EngineTasks.cs | 28 + .../Models/Engine/ConnectOptions.cs | 13 + .../Engine/Events/SessionProposalEvent.cs | 17 + .../Models/Engine/Methods/SessionEvent.cs | 3 + WalletConnectSharp.Sign/Models/Namespace.cs | 6 + .../WalletConnectSignClient.cs | 6 + .../Interfaces/IWeb3Wallet.cs | 3 +- .../Web3WalletClient.cs | 7 +- 26 files changed, 848 insertions(+), 52 deletions(-) create mode 100644 Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs delete mode 100644 WalletConnectSharp.Auth/AuthMetadata.cs delete mode 100644 WalletConnectSharp.Auth/Models/RedirectData.cs create mode 100644 WalletConnectSharp.Core/Models/RedirectData.cs create mode 100644 WalletConnectSharp.Sign/Models/Engine/Events/SessionProposalEvent.cs diff --git a/Tests/WalletConnectSharp.Auth.Tests/AuthClientFixture.cs b/Tests/WalletConnectSharp.Auth.Tests/AuthClientFixture.cs index 4eacafa..4776c4c 100644 --- a/Tests/WalletConnectSharp.Auth.Tests/AuthClientFixture.cs +++ b/Tests/WalletConnectSharp.Auth.Tests/AuthClientFixture.cs @@ -1,5 +1,6 @@ using WalletConnectSharp.Auth.Interfaces; using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Core; using WalletConnectSharp.Storage; using WalletConnectSharp.Tests.Common; @@ -17,7 +18,7 @@ protected override async void Init() { ProjectId = TestValues.TestProjectId, RelayUrl = TestValues.TestRelayUrl, - Metadata = new AuthMetadata() + Metadata = new Metadata() { Description = "An example dapp to showcase WalletConnectSharpv2", Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, @@ -32,7 +33,7 @@ protected override async void Init() { ProjectId = TestValues.TestProjectId, RelayUrl = TestValues.TestRelayUrl, - Metadata = new AuthMetadata() + Metadata = new Metadata() { Description = "An example wallet to showcase WalletConnectSharpv2", Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, diff --git a/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs b/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs index c135f3e..b1fd173 100644 --- a/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs +++ b/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs @@ -55,10 +55,10 @@ public async Task InitializeAsync() _dapp = await WalletConnectAuthClient.Init(new AuthOptions() { ProjectId = TestValues.TestProjectId, - Metadata = new AuthMetadata(), + Metadata = new Metadata(), Name = "dapp", }); - _wallet = await Web3WalletClient.Init(_core, new AuthMetadata(), "wallet"); + _wallet = await Web3WalletClient.Init(_core, new Metadata(), "wallet"); Assert.NotNull(_wallet); Assert.NotNull(_dapp); diff --git a/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs b/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs new file mode 100644 index 0000000..a529dc1 --- /dev/null +++ b/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs @@ -0,0 +1,668 @@ +using Nethereum.Hex.HexTypes; +using Nethereum.RPC.Eth.DTOs; +using Newtonsoft.Json; +using WalletConnectSharp.Auth; +using WalletConnectSharp.Auth.Internals; +using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Common.Model.Errors; +using WalletConnectSharp.Common.Utils; +using WalletConnectSharp.Core; +using WalletConnectSharp.Core.Models; +using WalletConnectSharp.Core.Models.Verify; +using WalletConnectSharp.Events; +using WalletConnectSharp.Network.Models; +using WalletConnectSharp.Sign; +using WalletConnectSharp.Sign.Models; +using WalletConnectSharp.Sign.Models.Engine; +using WalletConnectSharp.Sign.Models.Engine.Events; +using WalletConnectSharp.Tests.Common; +using Xunit; + +namespace WalletConnectSharp.Web3Wallet.Tests +{ + public class SignClientTests : IClassFixture, IAsyncLifetime + { + [RpcMethod("eth_signTransaction"), RpcRequestOptions(Clock.ONE_MINUTE, 99997)] + public class EthSignTransaction : List + { + } + + public class ChainChangedEvent + { + [JsonProperty("test")] + public string Test { get; set; } + } + + private static readonly string TestEthereumAddress = "0x3c582121909DE92Dc89A36898633C1aE4790382b"; + private static readonly string TestEthereumChain = "eip155:1"; + private static readonly string TestArbitrumChain = "eip155:42161"; + private static readonly string TestAvalancheChain = "eip155:43114"; + + private static readonly string[] TestAccounts = new[] + { + $"{TestEthereumChain}:{TestEthereumAddress}", $"{TestArbitrumChain}:{TestEthereumAddress}", + $"{TestAvalancheChain}:{TestEthereumAddress}" + }; + + private static readonly string[] TestEvents = new[] { "chainChanged", "accountsChanged" }; + + private static readonly RequestParams DefaultRequestParams = new RequestParams() + { + Aud = "http://localhost:3000/login", + Domain = "localhost:3000", + ChainId = "eip155:1", + Nonce = CryptoUtils.GenerateNonce() + }; + + private static readonly RequiredNamespaces TestRequiredNamespaces = new RequiredNamespaces() + { + { + "eip155", new RequiredNamespace() + .WithMethod("eth_signTransaction") + .WithChain("eip155:1") + .WithEvent("chainChanged") + } + }; + + private static readonly Namespaces TestUpdatedNamespaces = new Namespaces() + { + { + "eip155", new Namespace() + { + Methods = new [] + { + "eth_signTransaction", + "eth_sendTransaction", + "personal_sign", + "eth_signTypedData" + }, + Accounts = TestAccounts, + Events = TestEvents + } + } + }; + + private static readonly Namespace TestNamespace = new Namespace() + { + Methods = new[] { "eth_signTransaction", }, + Accounts = new[] { TestAccounts[0] }, + Events = new[] { TestEvents[0] } + }; + + private static readonly Namespaces TestNamespaces = new Namespaces() + { + { + "eip155", TestNamespace + } + }; + + private static readonly ConnectOptions TestConnectOptions = new ConnectOptions() + .UseRequireNamespaces(TestRequiredNamespaces); + + private readonly CryptoWalletFixture _cryptoWalletFixture; + private WalletConnectCore _core; + private WalletConnectSignClient _dapp; + private Web3WalletClient _wallet; + private string uriString; + private Task sessionApproval; + private SessionStruct session; + + + public string WalletAddress + { + get + { + return _cryptoWalletFixture.WalletAddress; + } + } + + public string Iss + { + get + { + return _cryptoWalletFixture.Iss; + } + } + + public SignClientTests(CryptoWalletFixture cryptoWalletFixture) + { + this._cryptoWalletFixture = cryptoWalletFixture; + } + + public async Task InitializeAsync() + { + _core = new WalletConnectCore(new CoreOptions() + { + ProjectId = TestValues.TestProjectId, RelayUrl = TestValues.TestRelayUrl, + }); + _dapp = await WalletConnectSignClient.Init(new SignClientOptions() + { + ProjectId = TestValues.TestProjectId, + Metadata = new Metadata(), + Name = "dapp", + }); + var connectData = await _dapp.Connect(TestConnectOptions); + uriString = connectData.Uri ?? ""; + sessionApproval = connectData.Approval; + + _wallet = await Web3WalletClient.Init(_core, new Metadata(), "wallet"); + + Assert.NotNull(_wallet); + Assert.NotNull(_dapp); + Assert.NotNull(_core); + Assert.Null(_wallet.Metadata.Redirect); + // Compiler knows ;) + //Assert.Null(_dapp.Metadata.Redirect); + } + + public async Task DisposeAsync() + { + if (_core.Relayer.Connected) + { + await _core.Relayer.TransportClose(); + } + } + + [Fact, Trait("Category", "unit")] + public async void TestShouldApproveSessionProposal() + { + TaskCompletionSource task1 = new TaskCompletionSource(); + _wallet.On(EngineEvents.SessionProposal, async (sender, @event) => + { + var id = @event.EventData.Id; + var proposal = @event.EventData.Proposal; + var verifyContext = @event.EventData.VerifiedContext; + + Assert.Equal(Validation.Unknown, verifyContext.Validation); + session = await _wallet.ApproveSession(id, TestNamespaces); + + Assert.Equal(proposal.RequiredNamespaces, TestRequiredNamespaces); + task1.TrySetResult(true); + }); + + await Task.WhenAll( + task1.Task, + sessionApproval, + _wallet.Pair(uriString) + ); + } + + [Fact, Trait("Category", "unit")] + public async void TestShouldRejectSessionProposal() + { + var rejectionError = Error.FromErrorType(ErrorType.USER_DISCONNECTED); + + TaskCompletionSource task1 = new TaskCompletionSource(); + _wallet.On(EngineEvents.SessionProposal, async (sender, @event) => + { + var proposal = @event.EventData.Proposal; + var id = @event.EventData.Id; + Assert.Equal(TestRequiredNamespaces, proposal.RequiredNamespaces); + await _wallet.RejectSession(id, rejectionError); + task1.TrySetResult(true); + }); + + async Task CheckSessionReject() + { + try + { + await sessionApproval; + } + catch (WalletConnectException e) + { + Assert.Equal(rejectionError.Code, e.Code); + Assert.Equal(rejectionError.Message, e.Message); + return; + } + Assert.Fail("Session approval task did not throw exception, expected rejection"); + } + + await Task.WhenAll( + task1.Task, + CheckSessionReject(), + _wallet.Pair(uriString) + ); + } + + [Fact, Trait("Category", "unit")] + public async void TestUpdateSession() + { + TaskCompletionSource task1 = new TaskCompletionSource(); + _wallet.On(EngineEvents.SessionProposal, async (sender, @event) => + { + var id = @event.EventData.Id; + var proposal = @event.EventData.Proposal; + var verifyContext = @event.EventData.VerifiedContext; + + Assert.Equal(Validation.Unknown, verifyContext.Validation); + session = await _wallet.ApproveSession(id, TestNamespaces); + + Assert.Equal(proposal.RequiredNamespaces, TestRequiredNamespaces); + task1.TrySetResult(true); + }); + + await Task.WhenAll( + task1.Task, + sessionApproval, + _wallet.Pair(uriString) + ); + + Assert.NotEqual(TestNamespaces, TestUpdatedNamespaces); + + TaskCompletionSource task2 = new TaskCompletionSource(); + _dapp.On(EngineEvents.SessionUpdate, (sender, @event) => + { + var param = @event.EventData.Params; + Assert.Equal(TestUpdatedNamespaces, param.Namespaces); + task2.TrySetResult(true); + }); + + await Task.WhenAll( + task2.Task, + _wallet.UpdateSession(session.Topic, TestUpdatedNamespaces) + ); + } + + [Fact, Trait("Category", "unit")] + public async void TestExtendSession() + { + TaskCompletionSource task1 = new TaskCompletionSource(); + _wallet.On(EngineEvents.SessionProposal, async (sender, @event) => + { + var id = @event.EventData.Id; + var proposal = @event.EventData.Proposal; + var verifyContext = @event.EventData.VerifiedContext; + + Assert.Equal(Validation.Unknown, verifyContext.Validation); + session = await _wallet.ApproveSession(id, TestNamespaces); + + Assert.Equal(proposal.RequiredNamespaces, TestRequiredNamespaces); + task1.TrySetResult(true); + }); + + await Task.WhenAll( + task1.Task, + sessionApproval, + _wallet.Pair(uriString) + ); + + var prevExpiry = session.Expiry; + var topic = session.Topic; + + // TODO Figure out if we need fake timers? + await Task.Delay(5000); + + await _wallet.ExtendSession(topic); + + var updatedExpiry = _wallet.Engine.SignClient.Session.Get(topic).Expiry; + + Assert.True(updatedExpiry > prevExpiry); + } + + [Fact, Trait("Category", "unit")] + public async void TestRespondToSessionRequest() + { + TaskCompletionSource task1 = new TaskCompletionSource(); + _wallet.On(EngineEvents.SessionProposal, async (sender, @event) => + { + var id = @event.EventData.Id; + var proposal = @event.EventData.Proposal; + var verifyContext = @event.EventData.VerifiedContext; + + session = await _wallet.ApproveSession(id, new Namespaces() + { + { + "eip155", new Namespace() + { + Methods = TestNamespace.Methods, + Events = TestNamespace.Events, + Accounts = new []{ $"{TestEthereumChain}:{WalletAddress}" } + } + } + }); + + Assert.Equal(proposal.RequiredNamespaces, TestRequiredNamespaces); + task1.TrySetResult(true); + }); + + await Task.WhenAll( + task1.Task, + sessionApproval, + _wallet.Pair(uriString) + ); + + TaskCompletionSource task2 = new TaskCompletionSource(); + _wallet.Engine.SignClient.Engine.SessionRequestEvents() + .OnRequest += async args => + { + var id = args.Request.Id; + var @params = args.Request; + var verifyContext = args.VerifiedContext; + var signTransaction = @params.Params[0]; + + Assert.Equal(verifyContext.Validation, Validation.Unknown); + + var signature = await _cryptoWalletFixture.CryptoWallet.GetAccount(0).TransactionManager + .SignTransactionAsync(signTransaction); + + args.Response = signature; + task2.TrySetResult(true); + }; + + async Task SendRequest() + { + var result = await _dapp.Request(session.Topic, + new EthSignTransaction() + { + new() + { + From = WalletAddress, + To = WalletAddress, + Data = "0x", + Nonce = new HexBigInteger("0x1"), + GasPrice = new HexBigInteger("0x020a7ac094"), + Gas = new HexBigInteger("0x5208"), + Value = new HexBigInteger("0x00") + } + }, TestEthereumChain); + + Assert.False(string.IsNullOrWhiteSpace(result)); + } + + await Task.WhenAll( + task2.Task, + SendRequest() + ); + } + + [Fact, Trait("Category", "unit")] + public async void TestWalletDisconnectFromSession() + { + TaskCompletionSource task1 = new TaskCompletionSource(); + _wallet.On(EngineEvents.SessionProposal, async (sender, @event) => + { + var id = @event.EventData.Id; + var proposal = @event.EventData.Proposal; + var verifyContext = @event.EventData.VerifiedContext; + + session = await _wallet.ApproveSession(id, new Namespaces() + { + { + "eip155", new Namespace() + { + Methods = TestNamespace.Methods, + Events = TestNamespace.Events, + Accounts = new []{ $"{TestEthereumChain}:{WalletAddress}" } + } + } + }); + + Assert.Equal(proposal.RequiredNamespaces, TestRequiredNamespaces); + task1.TrySetResult(true); + }); + + await Task.WhenAll( + task1.Task, + sessionApproval, + _wallet.Pair(uriString) + ); + + var reason = Error.FromErrorType(ErrorType.USER_DISCONNECTED); + + TaskCompletionSource task2 = new TaskCompletionSource(); + _dapp.On(EngineEvents.SessionDelete, (sender, @event) => + { + Assert.Equal(session.Topic, @event.EventData.Topic); + task2.TrySetResult(true); + }); + + await Task.WhenAll( + task2.Task, + _wallet.DisconnectSession(session.Topic, reason) + ); + } + + [Fact, Trait("Category", "unit")] + public async void TestDappDisconnectFromSession() + { + TaskCompletionSource task1 = new TaskCompletionSource(); + _wallet.On(EngineEvents.SessionProposal, async (sender, @event) => + { + var id = @event.EventData.Id; + var proposal = @event.EventData.Proposal; + var verifyContext = @event.EventData.VerifiedContext; + + session = await _wallet.ApproveSession(id, new Namespaces() + { + { + "eip155", new Namespace() + { + Methods = TestNamespace.Methods, + Events = TestNamespace.Events, + Accounts = new []{ $"{TestEthereumChain}:{WalletAddress}" } + } + } + }); + + Assert.Equal(proposal.RequiredNamespaces, TestRequiredNamespaces); + task1.TrySetResult(true); + }); + + await Task.WhenAll( + task1.Task, + sessionApproval, + _wallet.Pair(uriString) + ); + + var reason = Error.FromErrorType(ErrorType.USER_DISCONNECTED); + + TaskCompletionSource task2 = new TaskCompletionSource(); + _wallet.On(EngineEvents.SessionDelete, (sender, @event) => + { + Assert.Equal(session.Topic, @event.EventData.Topic); + task2.TrySetResult(true); + }); + + await Task.WhenAll( + task2.Task, + _dapp.Disconnect(session.Topic, reason) + ); + } + + [Fact, Trait("Category", "unit")] + public async void TestEmitSessionEvent() + { + TaskCompletionSource task1 = new TaskCompletionSource(); + _wallet.On(EngineEvents.SessionProposal, async (sender, @event) => + { + var id = @event.EventData.Id; + var proposal = @event.EventData.Proposal; + var verifyContext = @event.EventData.VerifiedContext; + + session = await _wallet.ApproveSession(id, new Namespaces() + { + { + "eip155", new Namespace() + { + Methods = TestNamespace.Methods, + Events = TestNamespace.Events, + Accounts = new []{ $"{TestEthereumChain}:{WalletAddress}" } + } + } + }); + + Assert.Equal(proposal.RequiredNamespaces, TestRequiredNamespaces); + task1.TrySetResult(true); + }); + + await Task.WhenAll( + task1.Task, + sessionApproval, + _wallet.Pair(uriString) + ); + + var sentData = new EventData() + { + Name = "chainChanged", + Data = new ChainChangedEvent() + { + Test = "123" + } + }; + + TaskCompletionSource task2 = new TaskCompletionSource(); + _dapp.HandleEventMessageType(async (s, request) => + { + var eventData = request.Params.Event; + var topic = request.Params.Topic; + Assert.Equal(session.Topic, topic); + Assert.Equal(sentData, eventData); + task2.TrySetResult(true); + }, null); + + await Task.WhenAll( + task2.Task, + _wallet.EmitSessionEvent(session.Topic, sentData, TestRequiredNamespaces["eip155"].Chains[0]) + ); + } + + [Fact, Trait("Category", "unit")] + public async void TestGetActiveSessions() + { + TaskCompletionSource task1 = new TaskCompletionSource(); + _wallet.On(EngineEvents.SessionProposal, async (sender, @event) => + { + var id = @event.EventData.Id; + var proposal = @event.EventData.Proposal; + var verifyContext = @event.EventData.VerifiedContext; + + session = await _wallet.ApproveSession(id, new Namespaces() + { + { + "eip155", new Namespace() + { + Methods = TestNamespace.Methods, + Events = TestNamespace.Events, + Accounts = new []{ $"{TestEthereumChain}:{WalletAddress}" } + } + } + }); + + Assert.Equal(proposal.RequiredNamespaces, TestRequiredNamespaces); + task1.TrySetResult(true); + }); + + await Task.WhenAll( + task1.Task, + sessionApproval, + _wallet.Pair(uriString) + ); + + var sessions = _wallet.ActiveSessions; + Assert.NotNull(sessions); + Assert.Single(sessions); + Assert.Equal(session.Topic, sessions.Values.ToArray()[0].Topic); + } + + public async void TestGetPendingSessionProposals() + { + TaskCompletionSource task1 = new TaskCompletionSource(); + _wallet.On(EngineEvents.SessionProposal, async (sender, @event) => + { + var proposals = _wallet.PendingSessionProposals; + Assert.NotNull(proposals); + Assert.Single(proposals); + Assert.Equal(TestRequiredNamespaces, proposals.Values.ToArray()[0].RequiredNamespaces); + task1.TrySetResult(true); + }); + + await Task.WhenAll( + task1.Task, + _wallet.Pair(uriString) + ); + } + + public async void TestGetPendingSessionRequests() + { + TaskCompletionSource task1 = new TaskCompletionSource(); + _wallet.On(EngineEvents.SessionProposal, async (sender, @event) => + { + var id = @event.EventData.Id; + var proposal = @event.EventData.Proposal; + var verifyContext = @event.EventData.VerifiedContext; + + session = await _wallet.ApproveSession(id, new Namespaces() + { + { + "eip155", new Namespace() + { + Methods = TestNamespace.Methods, + Events = TestNamespace.Events, + Accounts = new []{ $"{TestEthereumChain}:{WalletAddress}" } + } + } + }); + + Assert.Equal(proposal.RequiredNamespaces, TestRequiredNamespaces); + task1.TrySetResult(true); + }); + + await Task.WhenAll( + task1.Task, + sessionApproval, + _wallet.Pair(uriString) + ); + + var requestParams = new EthSignTransaction() + { + new() + { + From = WalletAddress, + To = WalletAddress, + Data = "0x", + Nonce = new HexBigInteger("0x1"), + GasPrice = new HexBigInteger("0x020a7ac094"), + Gas = new HexBigInteger("0x5208"), + Value = new HexBigInteger("0x00") + } + }; + + TaskCompletionSource task2 = new TaskCompletionSource(); + _wallet.Engine.SignClient.Engine.SessionRequestEvents() + .OnRequest += async args => + { + // Get the pending session request, since that's what we're testing + var pendingRequests = _wallet.PendingSessionRequests; + var request = pendingRequests[0]; + + var id = request.Id; + var verifyContext = args.VerifiedContext; + + // Perform unsafe cast, all pending requests are stored as object type + var signTransaction = ((EthSignTransaction)request.Parameters.Request.Params)[0]; + + Assert.Equal(args.Request.Id, id); + Assert.Equal(verifyContext.Validation, Validation.Unknown); + + var signature = await _cryptoWalletFixture.CryptoWallet.GetAccount(0).TransactionManager + .SignTransactionAsync(signTransaction); + + args.Response = signature; + task2.TrySetResult(true); + }; + + async Task SendRequest() + { + var result = await _dapp.Request(session.Topic, + requestParams, TestEthereumChain); + + Assert.False(string.IsNullOrWhiteSpace(result)); + } + + await Task.WhenAll( + task2.Task, + SendRequest() + ); + } + } +} diff --git a/WalletConnectSharp.Auth/AuthMetadata.cs b/WalletConnectSharp.Auth/AuthMetadata.cs deleted file mode 100644 index 7addc22..0000000 --- a/WalletConnectSharp.Auth/AuthMetadata.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Newtonsoft.Json; -using WalletConnectSharp.Auth.Models; -using WalletConnectSharp.Core; - -namespace WalletConnectSharp.Auth; - -public class AuthMetadata : Metadata -{ - [JsonProperty("redirect")] - public RedirectData Redirect { get; set; } - - [JsonProperty("verifyUrl")] - public string VerifyUrl { get; set; } -} diff --git a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs index 82acf9c..6af1ca6 100644 --- a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs +++ b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs @@ -4,6 +4,7 @@ using WalletConnectSharp.Auth.Models; using WalletConnectSharp.Common.Model.Errors; using WalletConnectSharp.Common.Utils; +using WalletConnectSharp.Core; using WalletConnectSharp.Core.Models.Relay; using WalletConnectSharp.Core.Models.Verify; using WalletConnectSharp.Crypto.Models; @@ -331,7 +332,7 @@ await this.Client.Requests.Set(payload.Id, }); } - private async Task GetVerifyContext(string hash, AuthMetadata metadata) + private async Task GetVerifyContext(string hash, Metadata metadata) { var context = new VerifiedContext() { diff --git a/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs b/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs index 565018b..205c697 100644 --- a/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs +++ b/WalletConnectSharp.Auth/Interfaces/IAuthClient.cs @@ -1,5 +1,6 @@ using WalletConnectSharp.Auth.Models; using WalletConnectSharp.Common; +using WalletConnectSharp.Core; using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Events.Interfaces; @@ -11,7 +12,7 @@ public interface IAuthClient : IModule, IEvents, IAuthClientEvents int Version { get; } ICore Core { get; set; } - AuthMetadata Metadata { get; set; } + Metadata Metadata { get; set; } string ProjectId { get; set; } IStore AuthKeys { get; set; } IStore PairingTopics { get; set; } diff --git a/WalletConnectSharp.Auth/Models/AuthOptions.cs b/WalletConnectSharp.Auth/Models/AuthOptions.cs index 2ff2968..9c32001 100644 --- a/WalletConnectSharp.Auth/Models/AuthOptions.cs +++ b/WalletConnectSharp.Auth/Models/AuthOptions.cs @@ -1,11 +1,12 @@ -using WalletConnectSharp.Core.Interfaces; +using WalletConnectSharp.Core; +using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Core.Models; namespace WalletConnectSharp.Auth.Models; public class AuthOptions : CoreOptions { - public AuthMetadata Metadata { get; set; } + public Metadata Metadata { get; set; } public ICore Core { get; set; } } diff --git a/WalletConnectSharp.Auth/Models/RedirectData.cs b/WalletConnectSharp.Auth/Models/RedirectData.cs deleted file mode 100644 index ae3ee1e..0000000 --- a/WalletConnectSharp.Auth/Models/RedirectData.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Newtonsoft.Json; - -namespace WalletConnectSharp.Auth.Models; - -public class RedirectData -{ - [JsonProperty("native")] - public string Native { get; set; } - - [JsonProperty("universal")] - public string Universal { get; set; } -} diff --git a/WalletConnectSharp.Auth/Models/Requester.cs b/WalletConnectSharp.Auth/Models/Requester.cs index 596a4a6..b4a0553 100644 --- a/WalletConnectSharp.Auth/Models/Requester.cs +++ b/WalletConnectSharp.Auth/Models/Requester.cs @@ -1,11 +1,12 @@ using Newtonsoft.Json; +using WalletConnectSharp.Core; namespace WalletConnectSharp.Auth.Models; public class Requester { [JsonProperty("metadata")] - public AuthMetadata Metadata { get; set; } + public Metadata Metadata { get; set; } [JsonProperty("publicKey")] public string PublicKey { get; set; } diff --git a/WalletConnectSharp.Auth/WalletConnectAuthClient.cs b/WalletConnectSharp.Auth/WalletConnectAuthClient.cs index 40572c5..6ec8a79 100644 --- a/WalletConnectSharp.Auth/WalletConnectAuthClient.cs +++ b/WalletConnectSharp.Auth/WalletConnectAuthClient.cs @@ -53,7 +53,7 @@ public int Version public event EventHandler AuthResponded; public event EventHandler AuthError; public ICore Core { get; set; } - public AuthMetadata Metadata { get; set; } + public Metadata Metadata { get; set; } public string ProjectId { get; set; } public IStore AuthKeys { get; set; } public IStore PairingTopics { get; set; } diff --git a/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs b/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs index aa9e4c4..01332ab 100644 --- a/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs +++ b/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs @@ -154,7 +154,7 @@ async void InspectResponseRaw(object sender, GenericEvent e // ignored if we can't find anything in the history } } - + Events.ListenFor($"request_{method}", RequestCallback); Events.ListenFor($"response_{method}", ResponseCallback); diff --git a/WalletConnectSharp.Core/Metadata.cs b/WalletConnectSharp.Core/Metadata.cs index 9d59403..b67009f 100644 --- a/WalletConnectSharp.Core/Metadata.cs +++ b/WalletConnectSharp.Core/Metadata.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using WalletConnectSharp.Core.Models; namespace WalletConnectSharp.Core { @@ -31,5 +32,11 @@ public class Metadata /// [JsonProperty("icons")] public string[] Icons { get; set; } + + [JsonProperty("redirect")] + public RedirectData Redirect { get; set; } + + [JsonProperty("verifyUrl")] + public string VerifyUrl { get; set; } } } diff --git a/WalletConnectSharp.Core/Models/MessageHandler/RequestEventArgs.cs b/WalletConnectSharp.Core/Models/MessageHandler/RequestEventArgs.cs index 92ed156..853de09 100644 --- a/WalletConnectSharp.Core/Models/MessageHandler/RequestEventArgs.cs +++ b/WalletConnectSharp.Core/Models/MessageHandler/RequestEventArgs.cs @@ -1,4 +1,5 @@ -using WalletConnectSharp.Network.Models; +using WalletConnectSharp.Core.Models.Verify; +using WalletConnectSharp.Network.Models; namespace WalletConnectSharp.Sign.Models { @@ -38,11 +39,14 @@ public class RequestEventArgs /// This value will always override if the value is non-null /// public Error Error { get; set; } + + public VerifiedContext VerifiedContext { get; set; } - internal RequestEventArgs(string topic, JsonRpcRequest request) + internal RequestEventArgs(string topic, JsonRpcRequest request, VerifiedContext context) { Topic = topic; Request = request; + VerifiedContext = context; } } } diff --git a/WalletConnectSharp.Core/Models/MessageHandler/TypedEventHandler.cs b/WalletConnectSharp.Core/Models/MessageHandler/TypedEventHandler.cs index 843861f..747f1c6 100644 --- a/WalletConnectSharp.Core/Models/MessageHandler/TypedEventHandler.cs +++ b/WalletConnectSharp.Core/Models/MessageHandler/TypedEventHandler.cs @@ -1,4 +1,8 @@ -using WalletConnectSharp.Core.Interfaces; +using Newtonsoft.Json; +using WalletConnectSharp.Common.Utils; +using WalletConnectSharp.Core; +using WalletConnectSharp.Core.Interfaces; +using WalletConnectSharp.Core.Models.Verify; using WalletConnectSharp.Network.Models; namespace WalletConnectSharp.Sign.Models @@ -205,7 +209,13 @@ protected virtual Task ResponseCallback(string arg1, JsonRpcResponse arg2) protected virtual async Task RequestCallback(string arg1, JsonRpcRequest arg2) { - var rea = new RequestEventArgs(arg1, arg2); + // Find pairing to get metadata + var pairing = _ref.Pairing.Store.Get(arg1); + + var hash = HashUtils.HashMessage(JsonConvert.SerializeObject(arg2)); + var verifyContext = await VerifyContext(hash, pairing.PeerMetadata); + + var rea = new RequestEventArgs(arg1, arg2, verifyContext); if (requestPredicate != null && !requestPredicate(rea)) return; if (_onRequest == null) return; @@ -217,5 +227,31 @@ protected virtual async Task RequestCallback(string arg1, JsonRpcRequest arg2 await _ref.MessageHandler.SendResult(arg2.Id, arg1, rea.Response); } } + + async Task VerifyContext(string hash, Metadata metadata) + { + var context = new VerifiedContext() + { + VerifyUrl = metadata.VerifyUrl ?? "", + Validation = Validation.Unknown, + Origin = metadata.Url ?? "" + }; + + try + { + var origin = await _ref.Verify.Resolve(hash); + if (!string.IsNullOrWhiteSpace(origin)) + { + context.Origin = origin; + context.Validation = origin == metadata.Url ? Validation.Valid : Validation.Invalid; + } + } + catch (Exception e) + { + // TODO Log to logger + } + + return context; + } } } diff --git a/WalletConnectSharp.Core/Models/RedirectData.cs b/WalletConnectSharp.Core/Models/RedirectData.cs new file mode 100644 index 0000000..f7e4c8d --- /dev/null +++ b/WalletConnectSharp.Core/Models/RedirectData.cs @@ -0,0 +1,11 @@ +using Newtonsoft.Json; + +namespace WalletConnectSharp.Core.Models +{ + public class RedirectData + { + [JsonProperty("native")] public string Native { get; set; } + + [JsonProperty("universal")] public string Universal { get; set; } + } +} diff --git a/WalletConnectSharp.Sign/Engine.cs b/WalletConnectSharp.Sign/Engine.cs index 76c5848..4bd359f 100644 --- a/WalletConnectSharp.Sign/Engine.cs +++ b/WalletConnectSharp.Sign/Engine.cs @@ -7,6 +7,7 @@ using WalletConnectSharp.Core.Models.Expirer; using WalletConnectSharp.Core.Models.Pairing; using WalletConnectSharp.Core.Models.Relay; +using WalletConnectSharp.Core.Models.Verify; using WalletConnectSharp.Events; using WalletConnectSharp.Events.Interfaces; using WalletConnectSharp.Events.Model; @@ -311,10 +312,16 @@ public async Task Pair(string uri) TaskCompletionSource sessionProposeTask = new TaskCompletionSource(); Client.Once(EngineEvents.SessionProposal, - delegate(object sender, GenericEvent> @event) + delegate(object sender, GenericEvent @event) { - var proposal = @event.EventData.Params; - if (topic == proposal.PairingTopic) + var proposal = @event.EventData.Proposal; + if (topic != proposal.PairingTopic) + return; + + if (@event.EventData.VerifiedContext.Validation == Validation.Invalid) + sessionProposeTask.SetException(new Exception( + $"Could not validate, invalid validation status {@event.EventData.VerifiedContext.Validation} for origin {@event.EventData.VerifiedContext.Origin}")); + else sessionProposeTask.SetResult(proposal); }); @@ -597,7 +604,8 @@ public async Task Emit(string topic, EventData @event, string chainId = nu await MessageHandler.SendRequest, object>(topic, new SessionEvent() { ChainId = chainId, - Event = @event + Event = @event, + Topic = topic, }); } diff --git a/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs b/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs index 0273c4a..2d4fb4e 100644 --- a/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs +++ b/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs @@ -3,6 +3,7 @@ using WalletConnectSharp.Sign.Models; using WalletConnectSharp.Sign.Models.Engine; using WalletConnectSharp.Sign.Models.Engine.Events; +using WalletConnectSharp.Sign.Models.Engine.Methods; namespace WalletConnectSharp.Sign.Interfaces { @@ -154,5 +155,8 @@ public interface IEngineAPI /// The required namespaces the session must have to be returned /// All sessions that have a namespace that match the given SessionStruct[] Find(RequiredNamespaces requiredNamespaces); + + void HandleEventMessageType(Func>, Task> requestCallback, + Func, Task> responseCallback); } } diff --git a/WalletConnectSharp.Sign/Internals/EngineHandler.cs b/WalletConnectSharp.Sign/Internals/EngineHandler.cs index 2dda682..cf36b5b 100644 --- a/WalletConnectSharp.Sign/Internals/EngineHandler.cs +++ b/WalletConnectSharp.Sign/Internals/EngineHandler.cs @@ -1,4 +1,5 @@ -using WalletConnectSharp.Common.Model.Errors; +using Newtonsoft.Json; +using WalletConnectSharp.Common.Model.Errors; using WalletConnectSharp.Common.Utils; using WalletConnectSharp.Core.Models.Expirer; using WalletConnectSharp.Core.Models.Pairing.Methods; @@ -61,10 +62,13 @@ async Task IEnginePrivate.OnSessionProposeRequest(string topic, JsonRpcRequest() + var hash = HashUtils.HashMessage(JsonConvert.SerializeObject(payload)); + var verifyContext = await this.VerifyContext(hash, proposal.Proposer.Metadata); + this.Client.Events.Trigger(EngineEvents.SessionProposal, new SessionProposalEvent() { Id = id, - Params = proposal + Proposal = proposal, + VerifiedContext = verifyContext }); } catch (WalletConnectException e) diff --git a/WalletConnectSharp.Sign/Internals/EngineTasks.cs b/WalletConnectSharp.Sign/Internals/EngineTasks.cs index 95d0da4..cabb0f4 100644 --- a/WalletConnectSharp.Sign/Internals/EngineTasks.cs +++ b/WalletConnectSharp.Sign/Internals/EngineTasks.cs @@ -2,9 +2,11 @@ using WalletConnectSharp.Common.Model.Errors; using WalletConnectSharp.Common.Model.Relay; using WalletConnectSharp.Common.Utils; +using WalletConnectSharp.Core; using WalletConnectSharp.Core.Models; using WalletConnectSharp.Core.Models.Pairing; using WalletConnectSharp.Core.Models.Relay; +using WalletConnectSharp.Core.Models.Verify; using WalletConnectSharp.Network.Models; using WalletConnectSharp.Sign.Interfaces; using WalletConnectSharp.Sign.Models; @@ -96,5 +98,31 @@ Task IEnginePrivate.Cleanup() ) ); } + + async Task VerifyContext(string hash, Metadata metadata) + { + var context = new VerifiedContext() + { + VerifyUrl = metadata.VerifyUrl ?? "", + Validation = Validation.Unknown, + Origin = metadata.Url ?? "" + }; + + try + { + var origin = await this.Client.Core.Verify.Resolve(hash); + if (!string.IsNullOrWhiteSpace(origin)) + { + context.Origin = origin; + context.Validation = origin == metadata.Url ? Validation.Valid : Validation.Invalid; + } + } + catch (Exception e) + { + // TODO Log to logger + } + + return context; + } } } diff --git a/WalletConnectSharp.Sign/Models/Engine/ConnectOptions.cs b/WalletConnectSharp.Sign/Models/Engine/ConnectOptions.cs index f19d501..0568c03 100644 --- a/WalletConnectSharp.Sign/Models/Engine/ConnectOptions.cs +++ b/WalletConnectSharp.Sign/Models/Engine/ConnectOptions.cs @@ -64,6 +64,19 @@ public ConnectOptions RequireNamespace(string chain, RequiredNamespace requiredN return this; } + + /// + /// Require a specific chain and namespace + /// + /// The chain the namespace exists in + /// The required namespace that must be present for this session + /// This object, acts a builder function + public ConnectOptions UseRequireNamespaces(RequiredNamespaces requiredNamespaces) + { + RequiredNamespaces = requiredNamespaces; + + return this; + } /// /// Include a pairing topic with these connect options. The pairing topic MUST exist in storage. diff --git a/WalletConnectSharp.Sign/Models/Engine/Events/SessionProposalEvent.cs b/WalletConnectSharp.Sign/Models/Engine/Events/SessionProposalEvent.cs new file mode 100644 index 0000000..4a10731 --- /dev/null +++ b/WalletConnectSharp.Sign/Models/Engine/Events/SessionProposalEvent.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; +using WalletConnectSharp.Core.Models.Verify; + +namespace WalletConnectSharp.Sign.Models.Engine.Events +{ + public class SessionProposalEvent + { + [JsonProperty("id")] + public long Id { get; set; } + + [JsonProperty("params")] + public ProposalStruct Proposal { get; set; } + + [JsonProperty("verifyContext")] + public VerifiedContext VerifiedContext { get; set; } + } +} diff --git a/WalletConnectSharp.Sign/Models/Engine/Methods/SessionEvent.cs b/WalletConnectSharp.Sign/Models/Engine/Methods/SessionEvent.cs index 9856cda..16c882b 100644 --- a/WalletConnectSharp.Sign/Models/Engine/Methods/SessionEvent.cs +++ b/WalletConnectSharp.Sign/Models/Engine/Methods/SessionEvent.cs @@ -21,6 +21,9 @@ public class SessionEvent : IWcMethod [JsonProperty("chainId")] public string ChainId { get; set; } + [JsonProperty("topic")] + public string Topic { get; set; } + /// /// The event data /// diff --git a/WalletConnectSharp.Sign/Models/Namespace.cs b/WalletConnectSharp.Sign/Models/Namespace.cs index 4395efe..2d58c38 100644 --- a/WalletConnectSharp.Sign/Models/Namespace.cs +++ b/WalletConnectSharp.Sign/Models/Namespace.cs @@ -25,5 +25,11 @@ public class Namespace /// [JsonProperty("events")] public string[] Events { get; set; } + + public Namespace WithMethod(string method) + { + Methods = Methods.Append(method).ToArray(); + return this; + } } } diff --git a/WalletConnectSharp.Sign/WalletConnectSignClient.cs b/WalletConnectSharp.Sign/WalletConnectSignClient.cs index d642ede..e31ce51 100644 --- a/WalletConnectSharp.Sign/WalletConnectSignClient.cs +++ b/WalletConnectSharp.Sign/WalletConnectSignClient.cs @@ -13,6 +13,7 @@ using WalletConnectSharp.Sign.Models; using WalletConnectSharp.Sign.Models.Engine; using WalletConnectSharp.Sign.Models.Engine.Events; +using WalletConnectSharp.Sign.Models.Engine.Methods; using WalletConnectSharp.Storage; namespace WalletConnectSharp.Sign @@ -392,6 +393,11 @@ public SessionStruct[] Find(RequiredNamespaces requiredNamespaces) return Engine.Find(requiredNamespaces); } + public void HandleEventMessageType(Func>, Task> requestCallback, Func, Task> responseCallback) + { + this.Engine.HandleEventMessageType(requestCallback, responseCallback); + } + private async Task Initialize() { await this.Core.Start(); diff --git a/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3Wallet.cs b/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3Wallet.cs index d3ebd21..89283b3 100644 --- a/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3Wallet.cs +++ b/WalletConnectSharp.Web3Wallet/Interfaces/IWeb3Wallet.cs @@ -1,5 +1,6 @@ using WalletConnectSharp.Auth; using WalletConnectSharp.Common; +using WalletConnectSharp.Core; using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Events.Interfaces; @@ -11,5 +12,5 @@ public interface IWeb3Wallet : IModule, IEvents, IWeb3WalletApi ICore Core { get; } - AuthMetadata Metadata { get; } + Metadata Metadata { get; } } diff --git a/WalletConnectSharp.Web3Wallet/Web3WalletClient.cs b/WalletConnectSharp.Web3Wallet/Web3WalletClient.cs index 6aa82bf..5fdb56a 100644 --- a/WalletConnectSharp.Web3Wallet/Web3WalletClient.cs +++ b/WalletConnectSharp.Web3Wallet/Web3WalletClient.cs @@ -1,5 +1,6 @@ using WalletConnectSharp.Auth; using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Core; using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Events; using WalletConnectSharp.Network.Models; @@ -50,9 +51,9 @@ public IDictionary PendingAuthRequests public IWeb3WalletEngine Engine { get; } public ICore Core { get; } - public AuthMetadata Metadata { get; } + public Metadata Metadata { get; } - public static async Task Init(ICore core, AuthMetadata metadata, string name = null) + public static async Task Init(ICore core, Metadata metadata, string name = null) { var wallet = new Web3WalletClient(core, metadata, name); await wallet.Initialize(); @@ -60,7 +61,7 @@ public static async Task Init(ICore core, AuthMetadata metadat return wallet; } - private Web3WalletClient(ICore core, AuthMetadata metadata, string name = null) + private Web3WalletClient(ICore core, Metadata metadata, string name = null) { this.Metadata = metadata; this.Name = string.IsNullOrWhiteSpace(name) ? "Web3Wallet" : name; From c62d7a99293e2256cab40fb4164d261cdcaefbc4 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Thu, 15 Jun 2023 19:50:30 -0400 Subject: [PATCH 11/36] fix: auth unit tests in web3wallet --- .../Models/Error.cs | 65 +++++++++++++++++++ .../CryptoWalletFixture.cs | 2 +- .../AuthTests.cs | 23 ++++++- .../Controllers/AuthEngine.cs | 4 ++ .../Models/Verify/Verifier.cs | 17 +++-- .../Web3WalletClient.cs | 3 + 6 files changed, 105 insertions(+), 9 deletions(-) diff --git a/Core Modules/WalletConnectSharp.Network/Models/Error.cs b/Core Modules/WalletConnectSharp.Network/Models/Error.cs index c091df7..2f4b699 100644 --- a/Core Modules/WalletConnectSharp.Network/Models/Error.cs +++ b/Core Modules/WalletConnectSharp.Network/Models/Error.cs @@ -71,5 +71,70 @@ public WalletConnectException ToException() { return WalletConnectException.FromType((ErrorType)Code, Message); } + + private sealed class CodeMessageDataEqualityComparer : IEqualityComparer + { + public bool Equals(Error x, Error y) + { + if (ReferenceEquals(x, y)) + { + return true; + } + + if (ReferenceEquals(x, null)) + { + return false; + } + + if (ReferenceEquals(y, null)) + { + return false; + } + + if (x.GetType() != y.GetType()) + { + return false; + } + + return x.Code == y.Code && x.Message == y.Message && x.Data == y.Data; + } + + public int GetHashCode(Error obj) + { + return HashCode.Combine(obj.Code, obj.Message, obj.Data); + } + } + + public static IEqualityComparer CodeMessageDataComparer { get; } = new CodeMessageDataEqualityComparer(); + + protected bool Equals(Error other) + { + return Code == other.Code && Message == other.Message && Data == other.Data; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != this.GetType()) + { + return false; + } + + return Equals((Error)obj); + } + + public override int GetHashCode() + { + return HashCode.Combine(Code, Message, Data); + } } } diff --git a/Tests/WalletConnectSharp.Tests.Common/CryptoWalletFixture.cs b/Tests/WalletConnectSharp.Tests.Common/CryptoWalletFixture.cs index 12f24d4..eb581e2 100644 --- a/Tests/WalletConnectSharp.Tests.Common/CryptoWalletFixture.cs +++ b/Tests/WalletConnectSharp.Tests.Common/CryptoWalletFixture.cs @@ -13,7 +13,7 @@ public string WalletAddress { get { - return _wallet.GetAddresses(0)[0]; + return _wallet.GetAddresses(1)[0]; } } diff --git a/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs b/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs index b1fd173..fb70c84 100644 --- a/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs +++ b/Tests/WalletConnectSharp.Web3Wallet.Tests/AuthTests.cs @@ -4,6 +4,7 @@ using WalletConnectSharp.Core; using WalletConnectSharp.Core.Models; using WalletConnectSharp.Network.Models; +using WalletConnectSharp.Storage; using WalletConnectSharp.Tests.Common; using Xunit; @@ -51,14 +52,30 @@ public async Task InitializeAsync() _core = new WalletConnectCore(new CoreOptions() { ProjectId = TestValues.TestProjectId, RelayUrl = TestValues.TestRelayUrl, + Storage = new InMemoryStorage(), + Name = Guid.NewGuid().ToString(), }); _dapp = await WalletConnectAuthClient.Init(new AuthOptions() { ProjectId = TestValues.TestProjectId, - Metadata = new Metadata(), - Name = "dapp", + RelayUrl = TestValues.TestRelayUrl, + Metadata = new Metadata() + { + Description = "An example dapp to showcase WalletConnectSharpv2", + Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, + Name = $"WalletConnectSharpv2 Dapp Example - {Guid.NewGuid().ToString()}", + Url = "https://walletconnect.com" + }, + Name = $"dapp-{Guid.NewGuid().ToString()}", + Storage = new InMemoryStorage(), }); - _wallet = await Web3WalletClient.Init(_core, new Metadata(), "wallet"); + _wallet = await Web3WalletClient.Init(_core, new Metadata() + { + Description = "An example wallet to showcase WalletConnectSharpv2", + Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, + Name = $"WalletConnectSharpv2 Wallet Example - {Guid.NewGuid().ToString()}", + Url = "https://walletconnect.com" + }, $"wallet-{Guid.NewGuid().ToString()}"); Assert.NotNull(_wallet); Assert.NotNull(_dapp); diff --git a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs index 6af1ca6..52b301e 100644 --- a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs +++ b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs @@ -185,6 +185,10 @@ public async Task Respond(Message message, string iss) Cacao cacao; switch (message) { + case AuthErrorResponse errorResponse: + await this.SendError(id, responseTopic, + new ErrorResponse() { Error = errorResponse.Error, Id = errorResponse.Id }, encodeOptions); + return; case ErrorResponse errorResponse: await this.SendError(id, responseTopic, errorResponse, encodeOptions); return; diff --git a/WalletConnectSharp.Core/Models/Verify/Verifier.cs b/WalletConnectSharp.Core/Models/Verify/Verifier.cs index ceedfb3..18a18b6 100644 --- a/WalletConnectSharp.Core/Models/Verify/Verifier.cs +++ b/WalletConnectSharp.Core/Models/Verify/Verifier.cs @@ -17,12 +17,19 @@ public Verifier() public async Task Resolve(string attestationId) { - using HttpClient client = new HttpClient(); - var url = $"{VerifyServer}/attestation/{attestationId}"; - var results = await client.GetStringAsync(url); + try + { + using HttpClient client = new HttpClient(); + var url = $"{VerifyServer}/attestation/{attestationId}"; + var results = await client.GetStringAsync(url); - var verifiedContext = JsonConvert.DeserializeObject(results); + var verifiedContext = JsonConvert.DeserializeObject(results); - return verifiedContext != null ? verifiedContext.Origin : ""; + return verifiedContext != null ? verifiedContext.Origin : ""; + } + catch + { + return ""; + } } } diff --git a/WalletConnectSharp.Web3Wallet/Web3WalletClient.cs b/WalletConnectSharp.Web3Wallet/Web3WalletClient.cs index 5fdb56a..d428db5 100644 --- a/WalletConnectSharp.Web3Wallet/Web3WalletClient.cs +++ b/WalletConnectSharp.Web3Wallet/Web3WalletClient.cs @@ -64,6 +64,9 @@ public static async Task Init(ICore core, Metadata metadata, s private Web3WalletClient(ICore core, Metadata metadata, string name = null) { this.Metadata = metadata; + if (string.IsNullOrWhiteSpace(this.Metadata.Name)) + this.Metadata.Name = name; + this.Name = string.IsNullOrWhiteSpace(name) ? "Web3Wallet" : name; this.Context = $"{Name}-context"; this.Core = core; From 1231db6729f3c41db85ab2696b195e196288d339 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Fri, 16 Jun 2023 14:10:37 -0400 Subject: [PATCH 12/36] feat: all unit tests passing --- .../Utils/DictionaryComparer.cs | 30 +++++++++ .../Utils/Extensions.cs | 9 ++- .../Utils/ListComparer.cs | 21 ++++++ .../WebsocketConnection.cs | 2 + .../Interfaces/IRequestArguments.cs | 2 +- .../RelayTests.cs | 14 ++-- .../SignTests.cs | 60 ++++++++++++----- .../MessageHandler/TypedEventHandler.cs | 13 ++-- WalletConnectSharp.Sign/Engine.cs | 4 +- .../Interfaces/IEnginePrivate.cs | 2 +- WalletConnectSharp.Sign/Models/Namespace.cs | 65 +++++++++++++++++++ WalletConnectSharp.Sign/Models/Namespaces.cs | 34 +++++++++- .../Models/RequiredNamespace.cs | 65 +++++++++++++++++++ .../Models/RequiredNamespaces.cs | 45 ++++++++++++- .../Models/SessionRequestEventHandler.cs | 38 ++++++++--- .../Controllers/Web3WalletEngine.cs | 2 +- 16 files changed, 360 insertions(+), 46 deletions(-) create mode 100644 Core Modules/WalletConnectSharp.Common/Utils/DictionaryComparer.cs create mode 100644 Core Modules/WalletConnectSharp.Common/Utils/ListComparer.cs diff --git a/Core Modules/WalletConnectSharp.Common/Utils/DictionaryComparer.cs b/Core Modules/WalletConnectSharp.Common/Utils/DictionaryComparer.cs new file mode 100644 index 0000000..6ebe73c --- /dev/null +++ b/Core Modules/WalletConnectSharp.Common/Utils/DictionaryComparer.cs @@ -0,0 +1,30 @@ +namespace WalletConnectSharp.Common.Utils +{ + public class DictionaryComparer : + IEqualityComparer> + { + private IEqualityComparer valueComparer; + public DictionaryComparer(IEqualityComparer valueComparer = null) + { + this.valueComparer = valueComparer ?? EqualityComparer.Default; + } + public bool Equals(Dictionary x, Dictionary y) + { + if (x.Count != y.Count) + return false; + if (x.Keys.Except(y.Keys).Any()) + return false; + if (y.Keys.Except(x.Keys).Any()) + return false; + foreach (var pair in x) + if (!valueComparer.Equals(pair.Value, y[pair.Key])) + return false; + return true; + } + + public int GetHashCode(Dictionary obj) + { + throw new NotImplementedException(); + } + } +} diff --git a/Core Modules/WalletConnectSharp.Common/Utils/Extensions.cs b/Core Modules/WalletConnectSharp.Common/Utils/Extensions.cs index 4d562c5..63b210e 100644 --- a/Core Modules/WalletConnectSharp.Common/Utils/Extensions.cs +++ b/Core Modules/WalletConnectSharp.Common/Utils/Extensions.cs @@ -129,5 +129,12 @@ public static async Task WithTimeout(this Task task, TimeSpan timeout, string me throw new TimeoutException(message.Replace("%t", timeout.ToString())); } } + + public static bool SetEquals(this IEnumerable first, IEnumerable second, + IEqualityComparer comparer) + { + return new HashSet(second, comparer ?? EqualityComparer.Default) + .SetEquals(first); + } } -} \ No newline at end of file +} diff --git a/Core Modules/WalletConnectSharp.Common/Utils/ListComparer.cs b/Core Modules/WalletConnectSharp.Common/Utils/ListComparer.cs new file mode 100644 index 0000000..0a485f2 --- /dev/null +++ b/Core Modules/WalletConnectSharp.Common/Utils/ListComparer.cs @@ -0,0 +1,21 @@ +namespace WalletConnectSharp.Common.Utils +{ + public class ListComparer : IEqualityComparer> + { + private IEqualityComparer valueComparer; + public ListComparer(IEqualityComparer valueComparer = null) + { + this.valueComparer = valueComparer ?? EqualityComparer.Default; + } + + public bool Equals(List x, List y) + { + return x.SetEquals(y, valueComparer); + } + + public int GetHashCode(List obj) + { + throw new NotImplementedException(); + } + } +} diff --git a/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnection.cs b/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnection.cs index 7cf134c..21e3d2d 100644 --- a/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnection.cs +++ b/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnection.cs @@ -222,6 +222,8 @@ private void OnPayload(ResponseMessage obj) } if (string.IsNullOrWhiteSpace(json)) return; + + //Console.WriteLine($"[{Name}] Got payload {json}"); Events.Trigger(WebsocketConnectionEvents.Payload, json); } diff --git a/Core Modules/WalletConnectSharp.Network/Interfaces/IRequestArguments.cs b/Core Modules/WalletConnectSharp.Network/Interfaces/IRequestArguments.cs index e7152fa..562c7ff 100644 --- a/Core Modules/WalletConnectSharp.Network/Interfaces/IRequestArguments.cs +++ b/Core Modules/WalletConnectSharp.Network/Interfaces/IRequestArguments.cs @@ -20,4 +20,4 @@ public interface IRequestArguments [JsonProperty("params")] T Params { get; } } -} \ No newline at end of file +} diff --git a/Tests/WalletConnectSharp.Network.Tests/RelayTests.cs b/Tests/WalletConnectSharp.Network.Tests/RelayTests.cs index 311234e..6700e29 100644 --- a/Tests/WalletConnectSharp.Network.Tests/RelayTests.cs +++ b/Tests/WalletConnectSharp.Network.Tests/RelayTests.cs @@ -17,12 +17,12 @@ namespace WalletConnectSharp.Network.Tests { public class RelayTests { - private static readonly JsonRpcRequest TEST_WAKU_REQUEST = + private static readonly JsonRpcRequest TEST_IRN_REQUEST = new JsonRpcRequest(RelayProtocols.DefaultProtocol.Subscribe, new TopicData() { Topic = "ca838d59a3a3fe3824dab9ca7882ac9a2227c5d0284c88655b261a2fe85db270" }); - private static readonly JsonRpcRequest TEST_BAD_WAKU_REQUEST = + private static readonly JsonRpcRequest TEST_BAD_IRN_REQUEST = new JsonRpcRequest(RelayProtocols.DefaultProtocol.Subscribe, new TopicData()); private static readonly string DEFAULT_GOOD_WS_URL = "wss://relay.walletconnect.com"; @@ -56,7 +56,7 @@ public async void ConnectAndRequest() var provider = new JsonRpcProvider(connection); await provider.Connect(); - var result = await provider.Request(TEST_WAKU_REQUEST); + var result = await provider.Request(TEST_IRN_REQUEST); Assert.True(result.Length > 0); } @@ -68,7 +68,7 @@ public async void RequestWithoutConnect() var connection = new WebsocketConnection(url); var provider = new JsonRpcProvider(connection); - var result = await provider.Request(TEST_WAKU_REQUEST); + var result = await provider.Request(TEST_IRN_REQUEST); Assert.True(result.Length > 0); } @@ -80,7 +80,7 @@ public async void ThrowOnJsonRpcError() var connection = new WebsocketConnection(url); var provider = new JsonRpcProvider(connection); - await Assert.ThrowsAsync(() => provider.Request(TEST_BAD_WAKU_REQUEST)); + await Assert.ThrowsAsync(() => provider.Request(TEST_BAD_IRN_REQUEST)); } [Fact, Trait("Category", "integration")] @@ -89,7 +89,7 @@ public async void ThrowsOnUnavailableHost() var connection = new WebsocketConnection(BAD_WS_URL); var provider = new JsonRpcProvider(connection); - await Assert.ThrowsAsync(() => provider.Request(TEST_WAKU_REQUEST)); + await Assert.ThrowsAsync(() => provider.Request(TEST_IRN_REQUEST)); } [Fact, Trait("Category", "integration")] @@ -102,7 +102,7 @@ public async void ReconnectsWithNewProvidedHost() await provider.Connect(url); Assert.Equal(url, provider.Connection.Url); - var result = await provider.Request(TEST_WAKU_REQUEST); + var result = await provider.Request(TEST_IRN_REQUEST); Assert.True(result.Length > 0); } diff --git a/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs b/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs index a529dc1..617d797 100644 --- a/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs +++ b/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs @@ -1,7 +1,7 @@ using Nethereum.Hex.HexTypes; using Nethereum.RPC.Eth.DTOs; +using Nethereum.Web3.Accounts; using Newtonsoft.Json; -using WalletConnectSharp.Auth; using WalletConnectSharp.Auth.Internals; using WalletConnectSharp.Auth.Models; using WalletConnectSharp.Common.Model.Errors; @@ -15,6 +15,7 @@ using WalletConnectSharp.Sign.Models; using WalletConnectSharp.Sign.Models.Engine; using WalletConnectSharp.Sign.Models.Engine.Events; +using WalletConnectSharp.Storage; using WalletConnectSharp.Tests.Common; using Xunit; @@ -22,7 +23,10 @@ namespace WalletConnectSharp.Web3Wallet.Tests { public class SignClientTests : IClassFixture, IAsyncLifetime { - [RpcMethod("eth_signTransaction"), RpcRequestOptions(Clock.ONE_MINUTE, 99997)] + [RpcMethod("eth_signTransaction"), + RpcRequestOptions(Clock.ONE_MINUTE, 99997), + RpcResponseOptions(Clock.ONE_MINUTE, 99996) + ] public class EthSignTransaction : List { } @@ -134,25 +138,39 @@ public async Task InitializeAsync() _core = new WalletConnectCore(new CoreOptions() { ProjectId = TestValues.TestProjectId, RelayUrl = TestValues.TestRelayUrl, + Name = $"wallet-csharp-test-{Guid.NewGuid().ToString()}", + Storage = new InMemoryStorage(), }); _dapp = await WalletConnectSignClient.Init(new SignClientOptions() { ProjectId = TestValues.TestProjectId, - Metadata = new Metadata(), - Name = "dapp", + Name = $"dapp-csharp-test-{Guid.NewGuid().ToString()}", + RelayUrl = TestValues.TestRelayUrl, + Metadata = new Metadata() + { + Description = "An example dapp to showcase WalletConnectSharpv2", + Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, + Name = $"dapp-csharp-test-{Guid.NewGuid().ToString()}", + Url = "https://walletconnect.com" + }, + Storage = new InMemoryStorage(), }); var connectData = await _dapp.Connect(TestConnectOptions); uriString = connectData.Uri ?? ""; sessionApproval = connectData.Approval; - _wallet = await Web3WalletClient.Init(_core, new Metadata(), "wallet"); + _wallet = await Web3WalletClient.Init(_core, new Metadata() + { + Description = "An example wallet to showcase WalletConnectSharpv2", + Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, + Name = $"wallet-csharp-test-{Guid.NewGuid().ToString()}", + Url = "https://walletconnect.com", + }, $"wallet-csharp-test-{Guid.NewGuid().ToString()}"); Assert.NotNull(_wallet); Assert.NotNull(_dapp); Assert.NotNull(_core); Assert.Null(_wallet.Metadata.Redirect); - // Compiler knows ;) - //Assert.Null(_dapp.Metadata.Redirect); } public async Task DisposeAsync() @@ -219,8 +237,8 @@ async Task CheckSessionReject() await Task.WhenAll( task1.Task, - CheckSessionReject(), - _wallet.Pair(uriString) + _wallet.Pair(uriString), + CheckSessionReject() ); } @@ -333,20 +351,22 @@ await Task.WhenAll( TaskCompletionSource task2 = new TaskCompletionSource(); _wallet.Engine.SignClient.Engine.SessionRequestEvents() - .OnRequest += async args => + .OnRequest += args => { var id = args.Request.Id; var @params = args.Request; var verifyContext = args.VerifiedContext; var signTransaction = @params.Params[0]; - Assert.Equal(verifyContext.Validation, Validation.Unknown); + Assert.Equal(Validation.Unknown, verifyContext.Validation); - var signature = await _cryptoWalletFixture.CryptoWallet.GetAccount(0).TransactionManager - .SignTransactionAsync(signTransaction); + var signature = ((AccountSignerTransactionManager)_cryptoWalletFixture.CryptoWallet.GetAccount(0).TransactionManager) + .SignTransaction(signTransaction); args.Response = signature; task2.TrySetResult(true); + + return Task.CompletedTask; }; async Task SendRequest() @@ -516,7 +536,8 @@ await Task.WhenAll( var eventData = request.Params.Event; var topic = request.Params.Topic; Assert.Equal(session.Topic, topic); - Assert.Equal(sentData, eventData); + Assert.Equal(sentData.Name, eventData.Name); + Assert.Equal(sentData.Data.Test, eventData.Data.Test); task2.TrySetResult(true); }, null); @@ -564,6 +585,7 @@ await Task.WhenAll( Assert.Equal(session.Topic, sessions.Values.ToArray()[0].Topic); } + [Fact, Trait("Category", "unit")] public async void TestGetPendingSessionProposals() { TaskCompletionSource task1 = new TaskCompletionSource(); @@ -582,6 +604,7 @@ await Task.WhenAll( ); } + [Fact, Trait("Category", "unit")] public async void TestGetPendingSessionRequests() { TaskCompletionSource task1 = new TaskCompletionSource(); @@ -629,7 +652,7 @@ await Task.WhenAll( TaskCompletionSource task2 = new TaskCompletionSource(); _wallet.Engine.SignClient.Engine.SessionRequestEvents() - .OnRequest += async args => + .OnRequest += args => { // Get the pending session request, since that's what we're testing var pendingRequests = _wallet.PendingSessionRequests; @@ -642,13 +665,14 @@ await Task.WhenAll( var signTransaction = ((EthSignTransaction)request.Parameters.Request.Params)[0]; Assert.Equal(args.Request.Id, id); - Assert.Equal(verifyContext.Validation, Validation.Unknown); + Assert.Equal(Validation.Unknown, verifyContext.Validation); - var signature = await _cryptoWalletFixture.CryptoWallet.GetAccount(0).TransactionManager - .SignTransactionAsync(signTransaction); + var signature = ((AccountSignerTransactionManager)_cryptoWalletFixture.CryptoWallet.GetAccount(0).TransactionManager) + .SignTransaction(signTransaction); args.Response = signature; task2.TrySetResult(true); + return Task.CompletedTask; }; async Task SendRequest() diff --git a/WalletConnectSharp.Core/Models/MessageHandler/TypedEventHandler.cs b/WalletConnectSharp.Core/Models/MessageHandler/TypedEventHandler.cs index 747f1c6..2f0cb34 100644 --- a/WalletConnectSharp.Core/Models/MessageHandler/TypedEventHandler.cs +++ b/WalletConnectSharp.Core/Models/MessageHandler/TypedEventHandler.cs @@ -209,12 +209,17 @@ protected virtual Task ResponseCallback(string arg1, JsonRpcResponse arg2) protected virtual async Task RequestCallback(string arg1, JsonRpcRequest arg2) { + VerifiedContext verifyContext = new VerifiedContext() { Validation = Validation.Unknown }; + // Find pairing to get metadata - var pairing = _ref.Pairing.Store.Get(arg1); + if (_ref.Pairing.Store.Keys.Contains(arg1)) + { + var pairing = _ref.Pairing.Store.Get(arg1); + + var hash = HashUtils.HashMessage(JsonConvert.SerializeObject(arg2)); + verifyContext = await VerifyContext(hash, pairing.PeerMetadata); + } - var hash = HashUtils.HashMessage(JsonConvert.SerializeObject(arg2)); - var verifyContext = await VerifyContext(hash, pairing.PeerMetadata); - var rea = new RequestEventArgs(arg1, arg2, verifyContext); if (requestPredicate != null && !requestPredicate(rea)) return; diff --git a/WalletConnectSharp.Sign/Engine.cs b/WalletConnectSharp.Sign/Engine.cs index 4bd359f..fcd1160 100644 --- a/WalletConnectSharp.Sign/Engine.cs +++ b/WalletConnectSharp.Sign/Engine.cs @@ -112,7 +112,7 @@ private void RegisterRelayerEvents() /// The managing events for the given types T, TR public TypedEventHandler SessionRequestEvents() { - return SessionRequestEventHandler.GetInstance(Client.Core); + return SessionRequestEventHandler.GetInstance(Client.Core, PrivateThis); } /// @@ -588,6 +588,8 @@ public async Task Respond(string topic, JsonRpcResponse response) { await MessageHandler.SendResult(id, topic, response.Result); } + + await PrivateThis.DeletePendingSessionRequest(id, new Error() { Code = 0, Message = "fulfilled" }); } /// diff --git a/WalletConnectSharp.Sign/Interfaces/IEnginePrivate.cs b/WalletConnectSharp.Sign/Interfaces/IEnginePrivate.cs index 7c16abb..9e0c4bd 100644 --- a/WalletConnectSharp.Sign/Interfaces/IEnginePrivate.cs +++ b/WalletConnectSharp.Sign/Interfaces/IEnginePrivate.cs @@ -7,7 +7,7 @@ namespace WalletConnectSharp.Sign.Interfaces { - internal interface IEnginePrivate + public interface IEnginePrivate { internal Task DeleteSession(string topic); diff --git a/WalletConnectSharp.Sign/Models/Namespace.cs b/WalletConnectSharp.Sign/Models/Namespace.cs index 2d58c38..1a2545e 100644 --- a/WalletConnectSharp.Sign/Models/Namespace.cs +++ b/WalletConnectSharp.Sign/Models/Namespace.cs @@ -31,5 +31,70 @@ public Namespace WithMethod(string method) Methods = Methods.Append(method).ToArray(); return this; } + + protected bool Equals(Namespace other) + { + return Equals(Accounts, other.Accounts) && Equals(Methods, other.Methods) && Equals(Events, other.Events); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != this.GetType()) + { + return false; + } + + return Equals((Namespace)obj); + } + + public override int GetHashCode() + { + return HashCode.Combine(Accounts, Methods, Events); + } + + private sealed class NamespaceEqualityComparer : IEqualityComparer + { + public bool Equals(Namespace x, Namespace y) + { + if (ReferenceEquals(x, y)) + { + return true; + } + + if (ReferenceEquals(x, null)) + { + return false; + } + + if (ReferenceEquals(y, null)) + { + return false; + } + + if (x.GetType() != y.GetType()) + { + return false; + } + + return x.Accounts.SequenceEqual(y.Accounts) && x.Methods.SequenceEqual(y.Methods) && x.Events.SequenceEqual(y.Events); + } + + public int GetHashCode(Namespace obj) + { + return HashCode.Combine(obj.Accounts, obj.Methods, obj.Events); + } + } + + public static IEqualityComparer NamespaceComparer { get; } = new NamespaceEqualityComparer(); } } diff --git a/WalletConnectSharp.Sign/Models/Namespaces.cs b/WalletConnectSharp.Sign/Models/Namespaces.cs index 0ec1d41..723b430 100644 --- a/WalletConnectSharp.Sign/Models/Namespaces.cs +++ b/WalletConnectSharp.Sign/Models/Namespaces.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using WalletConnectSharp.Common.Utils; namespace WalletConnectSharp.Sign.Models { @@ -9,5 +10,36 @@ namespace WalletConnectSharp.Sign.Models /// namespace: [-a-z0-9]{3,8} /// reference: [-_a-zA-Z0-9]{1,32} /// - public class Namespaces : Dictionary { } + public class Namespaces : Dictionary, IEquatable + { + public bool Equals(Namespaces other) + { + return new DictionaryComparer(Namespace.NamespaceComparer).Equals(this, other); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != this.GetType()) + { + return false; + } + + return Equals((Namespaces)obj); + } + + public override int GetHashCode() + { + throw new NotImplementedException(); + } + } } diff --git a/WalletConnectSharp.Sign/Models/RequiredNamespace.cs b/WalletConnectSharp.Sign/Models/RequiredNamespace.cs index 157a653..92b7525 100644 --- a/WalletConnectSharp.Sign/Models/RequiredNamespace.cs +++ b/WalletConnectSharp.Sign/Models/RequiredNamespace.cs @@ -69,5 +69,70 @@ public RequiredNamespace WithEvent(string @event) Events = Events.Append(@event).ToArray(); return this; } + + protected bool Equals(RequiredNamespace other) + { + return Equals(Chains, other.Chains) && Equals(Methods, other.Methods) && Equals(Events, other.Events); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != this.GetType()) + { + return false; + } + + return Equals((RequiredNamespace)obj); + } + + public override int GetHashCode() + { + return HashCode.Combine(Chains, Methods, Events); + } + + private sealed class RequiredNamespaceEqualityComparer : IEqualityComparer + { + public bool Equals(RequiredNamespace x, RequiredNamespace y) + { + if (ReferenceEquals(x, y)) + { + return true; + } + + if (ReferenceEquals(x, null)) + { + return false; + } + + if (ReferenceEquals(y, null)) + { + return false; + } + + if (x.GetType() != y.GetType()) + { + return false; + } + + return x.Chains.SequenceEqual(y.Chains) && x.Methods.SequenceEqual(y.Methods) && x.Events.SequenceEqual(y.Events); + } + + public int GetHashCode(RequiredNamespace obj) + { + return HashCode.Combine(obj.Chains, obj.Methods, obj.Events); + } + } + + public static IEqualityComparer RequiredNamespaceComparer { get; } = new RequiredNamespaceEqualityComparer(); } } diff --git a/WalletConnectSharp.Sign/Models/RequiredNamespaces.cs b/WalletConnectSharp.Sign/Models/RequiredNamespaces.cs index 8746315..c64c5ba 100644 --- a/WalletConnectSharp.Sign/Models/RequiredNamespaces.cs +++ b/WalletConnectSharp.Sign/Models/RequiredNamespaces.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using WalletConnectSharp.Common.Utils; namespace WalletConnectSharp.Sign.Models { @@ -9,5 +10,47 @@ namespace WalletConnectSharp.Sign.Models /// namespace: [-a-z0-9]{3,8} /// reference: [-_a-zA-Z0-9]{1,32} /// - public class RequiredNamespaces : Dictionary { } + public class RequiredNamespaces : Dictionary, IEquatable + { + public bool Equals(RequiredNamespaces other) + { + return new DictionaryComparer(RequiredNamespace.RequiredNamespaceComparer).Equals(this, other); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != this.GetType()) + { + return false; + } + + return Equals((RequiredNamespaces)obj); + } + + public override int GetHashCode() + { + throw new NotImplementedException(); + } + + + public bool Equals(RequiredNamespaces x, RequiredNamespaces y) + { + return new DictionaryComparer(RequiredNamespace.RequiredNamespaceComparer).Equals(x, y); + } + + public int GetHashCode(RequiredNamespaces obj) + { + throw new NotImplementedException(); + } + } } diff --git a/WalletConnectSharp.Sign/Models/SessionRequestEventHandler.cs b/WalletConnectSharp.Sign/Models/SessionRequestEventHandler.cs index 9806ce2..2cab77a 100644 --- a/WalletConnectSharp.Sign/Models/SessionRequestEventHandler.cs +++ b/WalletConnectSharp.Sign/Models/SessionRequestEventHandler.cs @@ -1,6 +1,5 @@ -using System; -using System.Threading.Tasks; -using WalletConnectSharp.Core.Interfaces; +using WalletConnectSharp.Core.Interfaces; +using WalletConnectSharp.Network.Models; using WalletConnectSharp.Sign.Interfaces; using WalletConnectSharp.Sign.Models.Engine.Methods; @@ -13,7 +12,9 @@ namespace WalletConnectSharp.Sign.Models /// The type of the session request /// The type of the response for the session request public class SessionRequestEventHandler : TypedEventHandler - { + { + private IEnginePrivate _enginePrivate; + /// /// Get a singleton instance of this class for the given context. The context /// string of the given will be used to determine the singleton instance to @@ -23,26 +24,27 @@ public class SessionRequestEventHandler : TypedEventHandler /// The engine this singleton instance is for, and where the context string will /// be read from /// The singleton instance to use for request/response event handlers - public static new TypedEventHandler GetInstance(ICore engine) + public static new TypedEventHandler GetInstance(ICore engine, IEnginePrivate _enginePrivate) { var context = engine.Context; if (_instances.ContainsKey(context)) return _instances[context]; - var _instance = new SessionRequestEventHandler(engine); + var _instance = new SessionRequestEventHandler(engine, _enginePrivate); _instances.Add(context, _instance); return _instance; } - protected SessionRequestEventHandler(ICore engine) : base(engine) + protected SessionRequestEventHandler(ICore engine, IEnginePrivate enginePrivate) : base(engine) { + this._enginePrivate = enginePrivate; } protected override TypedEventHandler BuildNew(ICore _ref, Func, bool> requestPredicate, Func, bool> responsePredicate) { - return new SessionRequestEventHandler(_ref) + return new SessionRequestEventHandler(_ref, _enginePrivate) { requestPredicate = requestPredicate, responsePredicate = responsePredicate @@ -62,12 +64,28 @@ private Task WrappedRefOnOnResponse(ResponseEventArgs e) return base.ResponseCallback(e.Topic, e.Response); } - private Task WrappedRefOnOnRequest(RequestEventArgs, TR> e) + private async Task WrappedRefOnOnRequest(RequestEventArgs, TR> e) { //Set inner request id to match outer request id e.Request.Params.Request.Id = e.Request.Id; - return base.RequestCallback(e.Topic, e.Request.Params.Request); + //Add to pending requests + //We can't do a simple cast, so we need to copy all the data + await _enginePrivate.SetPendingSessionRequest(new PendingRequestStruct() + { + Id = e.Request.Id, Parameters = new SessionRequest() + { + ChainId = e.Request.Params.ChainId, + Request = new JsonRpcRequest() + { + Id = e.Request.Params.Request.Id, + Method = e.Request.Params.Request.Method, + Params = e.Request.Params.Request.Params + } + }, Topic = e.Topic + }); + + await base.RequestCallback(e.Topic, e.Request.Params.Request); } } } diff --git a/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs b/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs index 2a1ba44..ecec6a4 100644 --- a/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs +++ b/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs @@ -175,7 +175,7 @@ private void PropagateEventToClient(string eventName, IEvents source) { EventHandler> eventHandler = (sender, @event) => { - this.Client.Events.TriggerType(eventName, @event, @event.GetType()); + this.Client.Events.TriggerType(eventName, @event.EventData, @event.EventData.GetType()); }; source.On(eventName, eventHandler); From 86450501cc9e4879f64b2769bc4a45a33d5bec86 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Fri, 16 Jun 2023 16:58:30 -0400 Subject: [PATCH 13/36] fix: stalling auth unit tests --- .../AuthClientFixture.cs | 50 ---------- .../AuthClientTest.cs | 97 +++++++++++-------- 2 files changed, 55 insertions(+), 92 deletions(-) delete mode 100644 Tests/WalletConnectSharp.Auth.Tests/AuthClientFixture.cs diff --git a/Tests/WalletConnectSharp.Auth.Tests/AuthClientFixture.cs b/Tests/WalletConnectSharp.Auth.Tests/AuthClientFixture.cs deleted file mode 100644 index 4776c4c..0000000 --- a/Tests/WalletConnectSharp.Auth.Tests/AuthClientFixture.cs +++ /dev/null @@ -1,50 +0,0 @@ -using WalletConnectSharp.Auth.Interfaces; -using WalletConnectSharp.Auth.Models; -using WalletConnectSharp.Core; -using WalletConnectSharp.Storage; -using WalletConnectSharp.Tests.Common; - -namespace WalletConnectSharp.Auth.Tests; - -public class AuthClientFixture : TwoClientsFixture -{ - public AuthOptions OptionsA { get; protected set; } - - public AuthOptions OptionsB { get; protected set; } - - protected override async void Init() - { - OptionsA = new AuthOptions() - { - ProjectId = TestValues.TestProjectId, - RelayUrl = TestValues.TestRelayUrl, - Metadata = new Metadata() - { - Description = "An example dapp to showcase WalletConnectSharpv2", - Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, - Name = "WalletConnectSharpv2 Dapp Example", - Url = "https://walletconnect.com" - }, - // Omit if you want persistant storage - Storage = new InMemoryStorage(), - }; - - OptionsB = new AuthOptions() - { - ProjectId = TestValues.TestProjectId, - RelayUrl = TestValues.TestRelayUrl, - Metadata = new Metadata() - { - Description = "An example wallet to showcase WalletConnectSharpv2", - Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, - Name = "WalletConnectSharpv2 Wallet Example", - Url = "https://walletconnect.com" - }, - // Omit if you want persistant storage - Storage = new InMemoryStorage() - }; - - ClientA = await WalletConnectAuthClient.Init(OptionsA); - ClientB = await WalletConnectAuthClient.Init(OptionsB); - } -} diff --git a/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs b/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs index 8bd8bbf..14d2a3a 100644 --- a/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs +++ b/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs @@ -3,18 +3,20 @@ using WalletConnectSharp.Auth.Interfaces; using WalletConnectSharp.Auth.Internals; using WalletConnectSharp.Auth.Models; +using WalletConnectSharp.Core; using WalletConnectSharp.Core.Models.Pairing; using WalletConnectSharp.Core.Models.Publisher; using WalletConnectSharp.Core.Models.Relay; using WalletConnectSharp.Core.Models.Verify; using WalletConnectSharp.Events; +using WalletConnectSharp.Storage; using WalletConnectSharp.Tests.Common; using Xunit; using ErrorResponse = WalletConnectSharp.Auth.Models.ErrorResponse; namespace WalletConnectSharp.Auth.Tests { - public class AuthClientTests : IClassFixture, IClassFixture + public class AuthClientTests : IClassFixture, IAsyncLifetime { private static readonly RequestParams DefaultRequestParams = new RequestParams() { @@ -24,24 +26,10 @@ public class AuthClientTests : IClassFixture, IClassFixture authRequested = new TaskCompletionSource(); @@ -132,8 +114,6 @@ public async void TestPairs() [Fact, Trait("Category", "unit")] public async void TestKnownPairings() { - await _authFixture.WaitForClientsReady(); - var ogSizeA = PeerA.Core.Pairing.Pairings.Length; var history = await PeerA.AuthHistory(); var ogHistorySizeA = history.Keys.Length; @@ -207,8 +187,6 @@ void OnPeerAOnAuthError(object sender, AuthErrorResponse args) [Fact, Trait("Category", "unit")] public async void HandlesAuthRequests() { - await _authFixture.WaitForClientsReady(); - var ogSize = PeerB.Requests.Length; TaskCompletionSource receivedAuthRequest = new TaskCompletionSource(); @@ -232,8 +210,6 @@ public async void HandlesAuthRequests() [Fact, Trait("Category", "unit")] public async void TestErrorResponses() { - await _authFixture.WaitForClientsReady(); - var ogPSize = PeerA.Core.Pairing.Pairings.Length; TaskCompletionSource errorResponse = new TaskCompletionSource(); @@ -273,8 +249,6 @@ void OnPeerAOnAuthResponded(object sender, AuthResponse response) [Fact, Trait("Category", "unit")] public async void HandlesSuccessfulResponse() { - await _authFixture.WaitForClientsReady(); - var ogPSize = PeerA.Core.Pairing.Pairings.Length; TaskCompletionSource successfulResponse = new TaskCompletionSource(); @@ -319,8 +293,6 @@ async void OnPeerBOnAuthRequested(object sender, AuthRequest request) [Fact, Trait("Category", "unit")] public async void TestCustomRequestExpiry() { - await _authFixture.WaitForClientsReady(); - var uri = ""; var expiry = 1000; @@ -360,8 +332,6 @@ async void OnPeerBOnAuthRequested(object sender, AuthRequest request) [Fact, Trait("Category", "unit")] public async void TestGetPendingPairings() { - await _authFixture.WaitForClientsReady(); - var ogCount = PeerB.PendingRequests.Count; TaskCompletionSource receivedAuthRequest = new TaskCompletionSource(); @@ -416,8 +386,6 @@ public async void TestGetPairings() [Fact, Trait("Category", "unit")] public async void TestPing() { - await _authFixture.WaitForClientsReady(); - TaskCompletionSource receivedAuthRequest = new TaskCompletionSource(); TaskCompletionSource receivedClientPing = new TaskCompletionSource(); TaskCompletionSource receivedPeerPing = new TaskCompletionSource(); @@ -457,8 +425,6 @@ public async void TestPing() [Fact, Trait("Category", "unit")] public async void TestDisconnectedPairing() { - await _authFixture.WaitForClientsReady(); - var peerAOgSize = PeerA.Core.Pairing.Pairings.Length; var peerBOgSize = PeerB.Core.Pairing.Pairings.Length; @@ -499,8 +465,6 @@ public async void TestDisconnectedPairing() [Fact, Trait("Category", "unit")] public async void TestReceivesMetadata() { - await _authFixture.WaitForClientsReady(); - var receivedMetadataName = ""; var ogPairingSize = PeerA.Core.Pairing.Pairings.Length; @@ -534,5 +498,54 @@ async void OnPeerBOnAuthRequested(object sender, AuthRequest request) Assert.Equal(PeerA.Metadata.Name, receivedMetadataName); PeerB.AuthRequested -= OnPeerBOnAuthRequested; } + + public async Task InitializeAsync() + { + var OptionsA = new AuthOptions() + { + ProjectId = TestValues.TestProjectId, + RelayUrl = TestValues.TestRelayUrl, + Metadata = new Metadata() + { + Description = "An example dapp to showcase WalletConnectSharpv2", + Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, + Name = $"WalletConnectSharpv2 Dapp Example - {Guid.NewGuid().ToString()}", + Url = "https://walletconnect.com" + }, + // Omit if you want persistant storage + Storage = new InMemoryStorage(), + }; + + var OptionsB = new AuthOptions() + { + ProjectId = TestValues.TestProjectId, + RelayUrl = TestValues.TestRelayUrl, + Metadata = new Metadata() + { + Description = "An example wallet to showcase WalletConnectSharpv2", + Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, + Name = $"WalletConnectSharpv2 Wallet Example - {Guid.NewGuid().ToString()}", + Url = "https://walletconnect.com" + }, + // Omit if you want persistant storage + Storage = new InMemoryStorage() + }; + + PeerA = await WalletConnectAuthClient.Init(OptionsA); + PeerB = await WalletConnectAuthClient.Init(OptionsB); + } + + public async Task DisposeAsync() + { + if (PeerA.Core.Relayer.Connected) + { + await PeerA.Core.Relayer.TransportClose(); + } + + if (PeerB.Core.Relayer.Connected) + { + await PeerB.Core.Relayer.TransportClose(); + } + } } } From 5e9babd97f9489a1bd93ce4c5a74865fb265af23 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Fri, 16 Jun 2023 17:04:50 -0400 Subject: [PATCH 14/36] use TrySetResult for sub creation result --- WalletConnectSharp.Core/Controllers/Relayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletConnectSharp.Core/Controllers/Relayer.cs b/WalletConnectSharp.Core/Controllers/Relayer.cs index 78f8802..06496de 100644 --- a/WalletConnectSharp.Core/Controllers/Relayer.cs +++ b/WalletConnectSharp.Core/Controllers/Relayer.cs @@ -315,7 +315,7 @@ public async Task Subscribe(string topic, SubscribeOptions opts = null) this.Subscriber.Once(Controllers.Subscriber.SubscriberEvents.Created, (sender, @event) => { if (@event.EventData.Topic == topic) - task1.SetResult(""); + task1.TrySetResult(""); }); return (await Task.WhenAll( From 00402dff7c934034e838fc66266f8a5d5a413347 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Sun, 9 Jul 2023 21:53:39 -0400 Subject: [PATCH 15/36] feat: start concurrency tests --- .../SignClientConcurrency.cs | 132 ++++++++++++++++++ .../WalletConnectSharp.Sign.Test.csproj | 4 + .../TestValues.cs | 18 +++ 3 files changed, 154 insertions(+) create mode 100644 Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs diff --git a/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs b/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs new file mode 100644 index 0000000..f9ed91c --- /dev/null +++ b/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs @@ -0,0 +1,132 @@ +using Newtonsoft.Json; +using WalletConnectSharp.Common.Utils; +using WalletConnectSharp.Events; +using WalletConnectSharp.Events.Model; +using WalletConnectSharp.Sign.Interfaces; +using WalletConnectSharp.Sign.Models; +using WalletConnectSharp.Sign.Models.Engine.Events; +using WalletConnectSharp.Sign.Models.Engine.Methods; +using WalletConnectSharp.Tests.Common; +using Xunit; +using Xunit.Abstractions; + +namespace WalletConnectSharp.Sign.Test +{ + + public class SignClientConcurrency + { + public class TestPairings + { + public SignClientFixture clients; + public SessionStruct sessionA; + } + + public class TestEmitData + { + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("data")] + public string Data { get; set; } + } + + private static readonly string TestEthereumAddress = "0x3c582121909DE92Dc89A36898633C1aE4790382b"; + private static readonly string TestEthereumChain = "eip155:1"; + private static readonly string TestArbitrumChain = "eip155:42161"; + private static readonly string TestAvalancheChain = "eip155:43114"; + + private static readonly string[] TestAccounts = new[] + { + $"{TestEthereumChain}:{TestEthereumAddress}", $"{TestArbitrumChain}:{TestEthereumAddress}", + $"{TestAvalancheChain}:{TestEthereumAddress}" + }; + + private static readonly string[] TestEvents = new[] { "chainChanged", "accountsChanged" }; + + private ITestOutputHelper _output; + + public SignClientConcurrency(ITestOutputHelper output) + { + this._output = output; + } + + [Fact, Trait("Category", "integration")] + public async void TestConcurrentClients() => await _TestConcurrentClients().WithTimeout(TimeSpan.FromMinutes(20)); + + private async Task _TestConcurrentClients() + { + List pairings = new List(); + List> messagesReceived = new List>(); + + CancellationTokenSource heartbeatToken = new CancellationTokenSource(); + +#pragma warning disable CS4014 + Task.Run(async delegate +#pragma warning restore CS4014 + { + while (!heartbeatToken.Token.IsCancellationRequested) + { + Log($"initialized pairs - {pairings.Count}"); + + await Task.Delay(TestValues.HeartbeatInterval); + } + }, heartbeatToken.Token); + + // TODO Do stuff + var testEventParams = new EventData() { Name = TestEvents[0], Data = "" }; + + Task ProcessMessages(TestPairings data, int clientIndex) + { + var clients = data.clients; + var sessionA = data.sessionA; + + var eventPayload = new SessionEvent() + { + ChainId = TestEthereumChain, + Event = testEventParams, + Topic = sessionA.Topic, + }; + + messagesReceived.Insert(clientIndex, new List()); + + TaskCompletionSource task = new TaskCompletionSource(); + + ISignClient[] clientsArr = new[] { clients.ClientA, clients.ClientB }; + + void CheckAllMessagesProcessed() + { + if (messagesReceived[clientIndex].Count >= TestValues.MessagesPerClient) + { + task.TrySetResult(true); + } + } + + foreach (var client in clientsArr) + { + client.On(EngineEvents.SessionPing, delegate(object sender, GenericEvent @event) + { + Assert.Equal(sessionA.Topic, @event.EventData.Topic); + messagesReceived[clientIndex].Add(@event.EventData); + CheckAllMessagesProcessed(); + }); + + client.On>(EngineEvents.SessionEvent, (sender, @event) => + { + Assert.Equal(testEventParams.Data, @event.EventData.Params.Event.Data); + Assert.Equal(eventPayload.Topic, @event.EventData.Topic); + messagesReceived[clientIndex].Add(@eve); + }) + } + + return task.Task; + } + + heartbeatToken.Cancel(); + } + + private void Log(string message) + { + this._output.WriteLine(message); + } + } +} diff --git a/Tests/WalletConnectSharp.Sign.Test/WalletConnectSharp.Sign.Test.csproj b/Tests/WalletConnectSharp.Sign.Test/WalletConnectSharp.Sign.Test.csproj index f0f04a4..729432d 100644 --- a/Tests/WalletConnectSharp.Sign.Test/WalletConnectSharp.Sign.Test.csproj +++ b/Tests/WalletConnectSharp.Sign.Test/WalletConnectSharp.Sign.Test.csproj @@ -25,4 +25,8 @@ + + + + diff --git a/Tests/WalletConnectSharp.Tests.Common/TestValues.cs b/Tests/WalletConnectSharp.Tests.Common/TestValues.cs index 9cd98b5..ab4dc14 100644 --- a/Tests/WalletConnectSharp.Tests.Common/TestValues.cs +++ b/Tests/WalletConnectSharp.Tests.Common/TestValues.cs @@ -10,7 +10,25 @@ public static class TestValues : DefaultProjectId; private const string DefaultRelayUrl = "wss://relay.walletconnect.com"; + private static readonly string EnvironmentRelayUrl = Environment.GetEnvironmentVariable("RELAY_ENDPOINT"); public static readonly string TestRelayUrl = !string.IsNullOrWhiteSpace(EnvironmentRelayUrl) ? EnvironmentRelayUrl : DefaultRelayUrl; + + private static readonly string EnvironmentClientCount = Environment.GetEnvironmentVariable("CLIENTS"); + public static readonly int ClientCount = !string.IsNullOrWhiteSpace(EnvironmentClientCount) + ? int.Parse(EnvironmentClientCount) + : 300000; + + private static readonly string EnvironmentMessageCount = Environment.GetEnvironmentVariable("MESSAGES_PER_CLIENT"); + public static readonly int MessagesPerClient = !string.IsNullOrWhiteSpace(EnvironmentMessageCount) + ? int.Parse(EnvironmentMessageCount) + : 1000; + + private static readonly string EnvironmentHeartbeatInterval = Environment.GetEnvironmentVariable("HEARTBEAT_INTERVAL"); + public static readonly int HeartbeatInterval = !string.IsNullOrWhiteSpace(EnvironmentHeartbeatInterval) + ? int.Parse(EnvironmentHeartbeatInterval) + : 3000; + + } } From ad72d4bdbc44052922ce1001d6e018b6bef1586f Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Sun, 9 Jul 2023 22:55:44 -0400 Subject: [PATCH 16/36] feat: complete concurrency unit tests --- .../WalletConnectSharp.Common/Utils/Clock.cs | 9 + .../SignClientConcurrency.cs | 173 +++++++++++++++++- .../WalletConnectSharp.Sign.Test/SignTests.cs | 20 +- WalletConnectSharp.Sign/Models/Namespaces.cs | 7 + WalletConnectSharpV2.sln | 6 + 5 files changed, 205 insertions(+), 10 deletions(-) diff --git a/Core Modules/WalletConnectSharp.Common/Utils/Clock.cs b/Core Modules/WalletConnectSharp.Common/Utils/Clock.cs index 28cea57..99913e0 100644 --- a/Core Modules/WalletConnectSharp.Common/Utils/Clock.cs +++ b/Core Modules/WalletConnectSharp.Common/Utils/Clock.cs @@ -164,6 +164,15 @@ public static long Now() { return ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds(); } + + /// + /// Current DateTime.Now as unix timestamp milliseconds + /// + /// + public static long NowMilliseconds() + { + return ((DateTimeOffset)DateTime.Now).ToUnixTimeMilliseconds(); + } public static TimeSpan AsTimeSpan(long seconds) { diff --git a/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs b/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs index f9ed91c..916bde9 100644 --- a/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs +++ b/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs @@ -1,7 +1,9 @@ using Newtonsoft.Json; +using WalletConnectSharp.Common.Model.Errors; using WalletConnectSharp.Common.Utils; using WalletConnectSharp.Events; using WalletConnectSharp.Events.Model; +using WalletConnectSharp.Network.Models; using WalletConnectSharp.Sign.Interfaces; using WalletConnectSharp.Sign.Models; using WalletConnectSharp.Sign.Models.Engine.Events; @@ -21,6 +23,13 @@ public class TestPairings public SessionStruct sessionA; } + public class TestResults + { + public long pairingLatencyMs; + public long handshakeLatencyMs; + public bool connected; + } + public class TestEmitData { [JsonProperty("name")] @@ -53,6 +62,42 @@ public SignClientConcurrency(ITestOutputHelper output) [Fact, Trait("Category", "integration")] public async void TestConcurrentClients() => await _TestConcurrentClients().WithTimeout(TimeSpan.FromMinutes(20)); + private int[][] BatchArray(int[] array, int size) + { + List results = new List(); + for (int i = 0; i < array.Length; i += size) + { + var batch = array.Skip(i).Take(size).ToArray(); + results.Add(batch); + } + + return results.ToArray(); + } + + private async Task InitTwoClients() + { + var fixture = new SignClientFixture(); + await fixture.WaitForClientsReady(); + await Task.Delay(500); + return fixture; + } + + private async Task DeleteClients(SignClientFixture clients) + { + await Task.Delay(500); + foreach (var client in new[] { clients.ClientA, clients.ClientB }) + { + if (client == null) + continue; + + // TODO Remove event data + if (client.Core.Relayer.Connected) + { + await client.Core.Relayer.TransportClose(); + } + } + } + private async Task _TestConcurrentClients() { List pairings = new List(); @@ -93,6 +138,23 @@ Task ProcessMessages(TestPairings data, int clientIndex) ISignClient[] clientsArr = new[] { clients.ClientA, clients.ClientB }; + var namespacesBefore = sessionA.Namespaces; + var namespacesAfter = new Namespaces(namespacesBefore) + { + { + "eip9001", new Namespace() { + Accounts = new []{ "eip9001:1:0x000000000000000000000000000000000000dead" }, + Methods = new []{ "eth_sendTransaction" }, + Events = new []{ "accountsChanged" } + } + } + }; + + Task Emit(ISignClient client) + { + return client.Emit(sessionA.Topic, testEventParams, TestEthereumChain); + } + void CheckAllMessagesProcessed() { if (messagesReceived[clientIndex].Count >= TestValues.MessagesPerClient) @@ -103,24 +165,127 @@ void CheckAllMessagesProcessed() foreach (var client in clientsArr) { - client.On(EngineEvents.SessionPing, delegate(object sender, GenericEvent @event) + client.On(EngineEvents.SessionPing, (sender, @event) => { Assert.Equal(sessionA.Topic, @event.EventData.Topic); messagesReceived[clientIndex].Add(@event.EventData); CheckAllMessagesProcessed(); }); - + client.On>(EngineEvents.SessionEvent, (sender, @event) => { Assert.Equal(testEventParams.Data, @event.EventData.Params.Event.Data); Assert.Equal(eventPayload.Topic, @event.EventData.Topic); - messagesReceived[clientIndex].Add(@eve); - }) + messagesReceived[clientIndex].Add(@event.EventData); + CheckAllMessagesProcessed(); + }); + + client.On(EngineEvents.SessionUpdate, (sender, @event) => + { + Assert.Equal(client.Session.Get(sessionA.Topic).Namespaces, namespacesAfter); + messagesReceived[clientIndex].Add(@event.EventData); + CheckAllMessagesProcessed(); + }); } + async void SendMessages() + { + Random random = new Random(); + for (int i = 0; i < TestValues.MessagesPerClient; i++) + { + var client = (int)Math.Floor(random.NextDouble() * clientsArr.Length); + await Emit(clientsArr[client]); + await Task.Delay(10); + } + } + + SendMessages(); + return task.Task; } + int[] arr = Enumerable.Range(0, TestValues.ClientCount+1).ToArray(); + int[][] batches = BatchArray(arr, 100); + + async Task ConnectClient() + { + var now = Clock.NowMilliseconds(); + var clients = await InitTwoClients(); + var handshakeLatencyMs = Clock.NowMilliseconds() - now; + await Task.Delay(10); + Assert.IsType(clients.ClientA); + Assert.IsType(clients.ClientB); + + var sessionA = await SignTests.TestConnectMethod(clients.ClientA, clients.ClientB); + pairings.Add(new TestPairings() + { + clients = clients, + sessionA = sessionA + }); + var pairingLatencyMs = Clock.NowMilliseconds() - now; + return new TestResults() + { + connected = true, handshakeLatencyMs = handshakeLatencyMs, pairingLatencyMs = pairingLatencyMs + }; + } + + foreach (int[] batch in batches) + { + var connections = (await Task.WhenAll( + batch.Select(async delegate(int i) + { + try + { + return await ConnectClient().WithTimeout(120000); + } + catch (TimeoutException) + { + Log($"Client {i} hung up"); + return new TestResults() + { + connected = false, handshakeLatencyMs = -1, pairingLatencyMs = -1 + }; + } + }) + )).Where(t => t.connected).ToList(); + + var averagePairingLatency = connections.Select(c => c.pairingLatencyMs) + .Aggregate((a, b) => a + b) / connections.Count; + var averageHandhsakeLatency = connections.Select(c => c.handshakeLatencyMs) + .Aggregate((a, b) => a + b) / connections.Count; + var failures = batch.Length - connections.Count; + Log($"{connections.Count} out of {batch.Length} connected ({averagePairingLatency}ms avg pairing latency, {averageHandhsakeLatency}ms avg handshake latency"); + + // TODO uploadLoadTestConnectionDataToCloudWatch + } + + await Task.WhenAll( + pairings.Select(async delegate(TestPairings testPairings, int i) + { + await ProcessMessages(testPairings, i); + }) + ); + + foreach (var data in pairings) + { + var clients = data.clients; + var sessionA = data.sessionA; + + TaskCompletionSource clientBDisconnected = new TaskCompletionSource(); + clients.ClientB.On(EngineEvents.SessionDelete, (sender, @event) => + { + Assert.Equal(sessionA.Topic, @event.EventData.Topic); + clientBDisconnected.TrySetResult(true); + }); + + await Task.WhenAll( + clientBDisconnected.Task, + clients.ClientA.Disconnect(sessionA.Topic, Error.FromErrorType(ErrorType.USER_DISCONNECTED)) + ); + + await DeleteClients(clients); + } + heartbeatToken.Cancel(); } diff --git a/Tests/WalletConnectSharp.Sign.Test/SignTests.cs b/Tests/WalletConnectSharp.Sign.Test/SignTests.cs index 6e324ef..2910703 100644 --- a/Tests/WalletConnectSharp.Sign.Test/SignTests.cs +++ b/Tests/WalletConnectSharp.Sign.Test/SignTests.cs @@ -1,6 +1,7 @@ using WalletConnectSharp.Common.Model.Errors; using WalletConnectSharp.Common.Utils; using WalletConnectSharp.Network.Models; +using WalletConnectSharp.Sign.Interfaces; using WalletConnectSharp.Sign.Models; using WalletConnectSharp.Sign.Models.Engine; using Xunit; @@ -45,11 +46,8 @@ public SignTests(SignClientFixture cryptoFixture) this._cryptoFixture = cryptoFixture; } - [Fact, Trait("Category", "integration")] - public async void TestApproveSession() + public static async Task TestConnectMethod(ISignClient clientA, ISignClient clientB) { - await _cryptoFixture.WaitForClientsReady(); - var testAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"; var dappConnectOptions = new ConnectOptions() { @@ -79,16 +77,26 @@ public async void TestApproveSession() } }; - var dappClient = ClientA; + var dappClient = clientA; var connectData = await dappClient.Connect(dappConnectOptions); - var walletClient = ClientB; + var walletClient = clientB; var proposal = await walletClient.Pair(connectData.Uri); var approveData = await walletClient.Approve(proposal, testAddress); var sessionData = await connectData.Approval; await approveData.Acknowledged(); + + return sessionData; + } + + [Fact, Trait("Category", "integration")] + public async void TestApproveSession() + { + await _cryptoFixture.WaitForClientsReady(); + + await TestConnectMethod(ClientA, ClientB); } [Fact, Trait("Category", "integration")] diff --git a/WalletConnectSharp.Sign/Models/Namespaces.cs b/WalletConnectSharp.Sign/Models/Namespaces.cs index 723b430..a079ae5 100644 --- a/WalletConnectSharp.Sign/Models/Namespaces.cs +++ b/WalletConnectSharp.Sign/Models/Namespaces.cs @@ -12,6 +12,13 @@ namespace WalletConnectSharp.Sign.Models /// public class Namespaces : Dictionary, IEquatable { + public Namespaces() : base() { } + + public Namespaces(Namespaces namespaces) : base(namespaces) + { + + } + public bool Equals(Namespaces other) { return new DictionaryComparer(Namespace.NamespaceComparer).Equals(this, other); diff --git a/WalletConnectSharpV2.sln b/WalletConnectSharpV2.sln index 35894ab..9a2fbcf 100644 --- a/WalletConnectSharpV2.sln +++ b/WalletConnectSharpV2.sln @@ -42,6 +42,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WalletConnectSharp.Web3Wall EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WalletConnectSharp.Web3Wallet.Tests", "Tests\WalletConnectSharp.Web3Wallet.Tests\WalletConnectSharp.Web3Wallet.Tests.csproj", "{82C44097-B520-45A9-8228-FF35E6DDDC9D}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Packages", "Packages", "{30D2D447-84B3-44B2-8CF8-F7DAE2AD30C5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -141,5 +143,9 @@ Global {C3CC2BC9-CD29-46B8-94DB-B104E427330B} = {3A55D6C7-0EF8-4EEA-90A5-89F5AFB97BA0} {7B63C8BD-737F-403F-AF66-8A2E7ADBDBAB} = {3A55D6C7-0EF8-4EEA-90A5-89F5AFB97BA0} {82C44097-B520-45A9-8228-FF35E6DDDC9D} = {3A55D6C7-0EF8-4EEA-90A5-89F5AFB97BA0} + {5189138F-FAE5-4B2C-912F-CEB429C156AC} = {30D2D447-84B3-44B2-8CF8-F7DAE2AD30C5} + {14F5A680-DC00-4889-B1C1-6E1BA46D2DE6} = {30D2D447-84B3-44B2-8CF8-F7DAE2AD30C5} + {7D5F3009-902E-4AF4-9EA2-992C8D41B6BF} = {30D2D447-84B3-44B2-8CF8-F7DAE2AD30C5} + {53622C78-1162-46A2-9FFE-8E90B018A144} = {30D2D447-84B3-44B2-8CF8-F7DAE2AD30C5} EndGlobalSection EndGlobal From 9abac0dbb55ce252287365ddaf7c901e865f4c34 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Mon, 10 Jul 2023 18:43:14 -0400 Subject: [PATCH 17/36] tests: start better sign connect test --- .../Shared/SignTestValues.cs | 77 +++++++++++++++++++ .../SignClientConcurrency.cs | 19 +---- .../SignClientFixture.cs | 10 ++- .../WalletConnectSharp.Sign.Test/SignTests.cs | 2 + .../WalletConnectSharp.Sign.Test.csproj | 4 - .../TwoClientsFixture.cs | 7 +- .../Controllers/JsonRpcHistoryFactory.cs | 14 +++- 7 files changed, 103 insertions(+), 30 deletions(-) create mode 100644 Tests/WalletConnectSharp.Sign.Test/Shared/SignTestValues.cs diff --git a/Tests/WalletConnectSharp.Sign.Test/Shared/SignTestValues.cs b/Tests/WalletConnectSharp.Sign.Test/Shared/SignTestValues.cs new file mode 100644 index 0000000..6116b3b --- /dev/null +++ b/Tests/WalletConnectSharp.Sign.Test/Shared/SignTestValues.cs @@ -0,0 +1,77 @@ +using WalletConnectSharp.Sign.Models; + +namespace WalletConnectSharp.Sign.Test.Shared { + + public static class SignTestValues + { + public static readonly string TestPolkadotAddress = "8cGfbK9Q4zbsNzhZsZUtpsQgX5LG2UCPEDuXYV33whktGt7"; + public static readonly string TestEthereumAddress = "0x3c582121909DE92Dc89A36898633C1aE4790382b"; + public static readonly string TestEthereumChain = "eip155:1"; + public static readonly string TestArbitrumChain = "eip155:42161"; + public static readonly string TestAvalancheChain = "eip155:43114"; + public static readonly string TestPolkadotChain = "polkadot:91b171bb158e2d3848fa23a9f1c25182"; + public static readonly string TestPolkadotAccount = $"{TestPolkadotChain}:{TestPolkadotAddress}"; + + public static readonly string[] TestAccounts = new[] + { + $"{TestEthereumChain}:{TestEthereumAddress}", $"{TestArbitrumChain}:{TestEthereumAddress}", + $"{TestAvalancheChain}:{TestEthereumAddress}" + }; + + public static readonly string[] TestMethods = new[] + { + "eth_sendTransaction", "eth_signTransaction", "personal_sign", "eth_signTypedData", + }; + + public static readonly string[] TestPolkadotMethods = new[] + { + "polkadot_signTransaction", + "polkadot_signMessage" + }; + + public static readonly string[] TestChains = new[] { TestEthereumChain, TestArbitrumChain, }; + + public static readonly string[] TestEvents = new[] { "chainChanged", "accountsChanged" }; + + public static readonly RequiredNamespaces TestRequiredNamespacees = new RequiredNamespaces() + { + { + "eip155", new RequiredNamespace() + { + Methods = TestMethods, + Events = TestEvents, + Chains = TestChains + } + } + }; + + public static readonly RequiredNamespaces TestRequiredNamespaceesV2 = new RequiredNamespaces() + { + { + "eip155", new RequiredNamespace() + { + Methods = TestMethods, + Events = TestEvents, + Chains = TestChains + } + }, + { + TestAvalancheChain, new RequiredNamespace() + { + Methods = TestMethods, + Events = TestEvents + } + } + }; + + public static readonly Namespaces TestOptionalNamespaces = new Namespaces() + { + { + "polkadot", new Namespace() + { + + } + } + }; + } +} diff --git a/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs b/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs index 916bde9..e0e6371 100644 --- a/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs +++ b/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs @@ -39,19 +39,6 @@ public class TestEmitData public string Data { get; set; } } - private static readonly string TestEthereumAddress = "0x3c582121909DE92Dc89A36898633C1aE4790382b"; - private static readonly string TestEthereumChain = "eip155:1"; - private static readonly string TestArbitrumChain = "eip155:42161"; - private static readonly string TestAvalancheChain = "eip155:43114"; - - private static readonly string[] TestAccounts = new[] - { - $"{TestEthereumChain}:{TestEthereumAddress}", $"{TestArbitrumChain}:{TestEthereumAddress}", - $"{TestAvalancheChain}:{TestEthereumAddress}" - }; - - private static readonly string[] TestEvents = new[] { "chainChanged", "accountsChanged" }; - private ITestOutputHelper _output; public SignClientConcurrency(ITestOutputHelper output) @@ -59,7 +46,7 @@ public SignClientConcurrency(ITestOutputHelper output) this._output = output; } - [Fact, Trait("Category", "integration")] + [Fact, Trait("Category", "concurrency")] public async void TestConcurrentClients() => await _TestConcurrentClients().WithTimeout(TimeSpan.FromMinutes(20)); private int[][] BatchArray(int[] array, int size) @@ -76,8 +63,8 @@ private int[][] BatchArray(int[] array, int size) private async Task InitTwoClients() { - var fixture = new SignClientFixture(); - await fixture.WaitForClientsReady(); + var fixture = new SignClientFixture(false); + await fixture.Init(); await Task.Delay(500); return fixture; } diff --git a/Tests/WalletConnectSharp.Sign.Test/SignClientFixture.cs b/Tests/WalletConnectSharp.Sign.Test/SignClientFixture.cs index 3533968..37b2cea 100644 --- a/Tests/WalletConnectSharp.Sign.Test/SignClientFixture.cs +++ b/Tests/WalletConnectSharp.Sign.Test/SignClientFixture.cs @@ -9,8 +9,12 @@ public class SignClientFixture : TwoClientsFixture { public SignClientOptions OptionsA { get; protected set; } public SignClientOptions OptionsB { get; protected set; } + + public SignClientFixture(bool initNow = true) : base(initNow) + { + } - protected override async void Init() + public override async Task Init() { OptionsA = new SignClientOptions() { @@ -20,7 +24,7 @@ protected override async void Init() { Description = "An example dapp to showcase WalletConnectSharpv2", Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, - Name = "WalletConnectSharpv2 Dapp Example", + Name = $"WalletConnectSharpv2 Dapp Example - {Guid.NewGuid().ToString()}", Url = "https://walletconnect.com" }, // Omit if you want persistant storage @@ -35,7 +39,7 @@ protected override async void Init() { Description = "An example wallet to showcase WalletConnectSharpv2", Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, - Name = "WalletConnectSharpv2 Wallet Example", + Name = $"WalletConnectSharpv2 Wallet Example - {Guid.NewGuid().ToString()}", Url = "https://walletconnect.com" }, // Omit if you want persistant storage diff --git a/Tests/WalletConnectSharp.Sign.Test/SignTests.cs b/Tests/WalletConnectSharp.Sign.Test/SignTests.cs index 2910703..3079010 100644 --- a/Tests/WalletConnectSharp.Sign.Test/SignTests.cs +++ b/Tests/WalletConnectSharp.Sign.Test/SignTests.cs @@ -48,6 +48,8 @@ public SignTests(SignClientFixture cryptoFixture) public static async Task TestConnectMethod(ISignClient clientA, ISignClient clientB) { + var start = Clock.NowMilliseconds(); + var testAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"; var dappConnectOptions = new ConnectOptions() { diff --git a/Tests/WalletConnectSharp.Sign.Test/WalletConnectSharp.Sign.Test.csproj b/Tests/WalletConnectSharp.Sign.Test/WalletConnectSharp.Sign.Test.csproj index 729432d..f0f04a4 100644 --- a/Tests/WalletConnectSharp.Sign.Test/WalletConnectSharp.Sign.Test.csproj +++ b/Tests/WalletConnectSharp.Sign.Test/WalletConnectSharp.Sign.Test.csproj @@ -25,8 +25,4 @@ - - - - diff --git a/Tests/WalletConnectSharp.Tests.Common/TwoClientsFixture.cs b/Tests/WalletConnectSharp.Tests.Common/TwoClientsFixture.cs index 0061649..e630033 100644 --- a/Tests/WalletConnectSharp.Tests.Common/TwoClientsFixture.cs +++ b/Tests/WalletConnectSharp.Tests.Common/TwoClientsFixture.cs @@ -6,12 +6,13 @@ public abstract class TwoClientsFixture public TClient ClientB { get; protected set; } - public TwoClientsFixture() + public TwoClientsFixture(bool initNow = true) { - Init(); + if (initNow) + Init(); } - protected abstract void Init(); + public abstract Task Init(); public async Task WaitForClientsReady() { diff --git a/WalletConnectSharp.Core/Controllers/JsonRpcHistoryFactory.cs b/WalletConnectSharp.Core/Controllers/JsonRpcHistoryFactory.cs index 3ff0fc2..c644341 100644 --- a/WalletConnectSharp.Core/Controllers/JsonRpcHistoryFactory.cs +++ b/WalletConnectSharp.Core/Controllers/JsonRpcHistoryFactory.cs @@ -23,6 +23,7 @@ public class JsonRpcHistoryFactory : IJsonRpcHistoryFactory /// The response type to store history for public class JsonRpcHistoryHolder { + private static readonly object historyLock = new object(); private static Dictionary> _instance = new Dictionary>(); /// @@ -33,11 +34,16 @@ public class JsonRpcHistoryHolder /// The singleton instance for the given ICore context public static async Task> InstanceForContext(ICore core) { - if (_instance.ContainsKey(core.Context)) - return _instance[core.Context]; + JsonRpcHistoryHolder historyHolder; + lock (historyLock) + { + if (_instance.ContainsKey(core.Context)) + return _instance[core.Context]; - var historyHolder = new JsonRpcHistoryHolder(core); - _instance.Add(core.Context, historyHolder); + historyHolder = new JsonRpcHistoryHolder(core); + _instance.Add(core.Context, historyHolder); + } + await historyHolder.History.Init(); return historyHolder; } From 2b5a8e32e3ba0d2d78543cac1a7532a42c707a3c Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Wed, 19 Jul 2023 13:53:14 -0400 Subject: [PATCH 18/36] feat: add support for custom websocket connections & bug fixes --- ...alletConnectSharp.Network.Websocket.csproj | 10 +++++++ .../WebsocketConnectionBuilder.cs | 12 ++++++++ .../Interfaces/IConnectionBuilder.cs | 7 +++++ .../FileSystemStorage.cs | 2 ++ .../SignClientFixture.cs | 6 ++-- .../WalletConnectSharp.Sign.Test.csproj | 1 + .../Controllers/Relayer.cs | 9 ++++-- WalletConnectSharp.Core/Interfaces/ICore.cs | 6 ++++ WalletConnectSharp.Core/Metadata.cs | 29 ++++++++----------- WalletConnectSharp.Core/Models/CoreOptions.cs | 9 ++++++ .../Models/RedirectData.cs | 5 ++-- WalletConnectSharp.Core/WalletConnectCore.cs | 6 ++++ .../WalletConnectSharp.Core.csproj | 1 + WalletConnectSharp.Sign/Engine.cs | 2 +- .../Interfaces/IEngineAPI.cs | 2 +- .../Models/Engine/ConnectOptions.cs | 8 +++-- .../Models/SignClientOptions.cs | 6 ++-- .../WalletConnectSignClient.cs | 6 ++-- .../Controllers/Web3WalletEngine.cs | 2 +- 19 files changed, 93 insertions(+), 36 deletions(-) create mode 100644 Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnectionBuilder.cs create mode 100644 Core Modules/WalletConnectSharp.Network/Interfaces/IConnectionBuilder.cs diff --git a/Core Modules/WalletConnectSharp.Network.Websocket/WalletConnectSharp.Network.Websocket.csproj b/Core Modules/WalletConnectSharp.Network.Websocket/WalletConnectSharp.Network.Websocket.csproj index 428bd01..6070f63 100644 --- a/Core Modules/WalletConnectSharp.Network.Websocket/WalletConnectSharp.Network.Websocket.csproj +++ b/Core Modules/WalletConnectSharp.Network.Websocket/WalletConnectSharp.Network.Websocket.csproj @@ -19,6 +19,16 @@ Apache-2.0 + + TRACE +WC_DEF_WEBSOCKET + + + + TRACE +WC_DEF_WEBSOCKET + + diff --git a/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnectionBuilder.cs b/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnectionBuilder.cs new file mode 100644 index 0000000..6fc818e --- /dev/null +++ b/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnectionBuilder.cs @@ -0,0 +1,12 @@ +using WalletConnectSharp.Network.Interfaces; + +namespace WalletConnectSharp.Network.Websocket +{ + public class WebsocketConnectionBuilder : IConnectionBuilder + { + public IJsonRpcConnection CreateConnection(string url) + { + return new WebsocketConnection(url); + } + } +} diff --git a/Core Modules/WalletConnectSharp.Network/Interfaces/IConnectionBuilder.cs b/Core Modules/WalletConnectSharp.Network/Interfaces/IConnectionBuilder.cs new file mode 100644 index 0000000..6892cd4 --- /dev/null +++ b/Core Modules/WalletConnectSharp.Network/Interfaces/IConnectionBuilder.cs @@ -0,0 +1,7 @@ +namespace WalletConnectSharp.Network.Interfaces +{ + public interface IConnectionBuilder + { + IJsonRpcConnection CreateConnection(string url); + } +} diff --git a/Core Modules/WalletConnectSharp.Storage/FileSystemStorage.cs b/Core Modules/WalletConnectSharp.Storage/FileSystemStorage.cs index 22685d6..58fda7d 100644 --- a/Core Modules/WalletConnectSharp.Storage/FileSystemStorage.cs +++ b/Core Modules/WalletConnectSharp.Storage/FileSystemStorage.cs @@ -119,6 +119,8 @@ private async Task Load() { // Move the file to a .unsupported file // and start fresh + if (File.Exists(FilePath + ".unsupported")) + File.Move(FilePath + ".unsupported", FilePath + "." + Guid.NewGuid() + ".unsupported"); File.Move(FilePath, FilePath + ".unsupported"); Entries = new Dictionary(); } diff --git a/Tests/WalletConnectSharp.Sign.Test/SignClientFixture.cs b/Tests/WalletConnectSharp.Sign.Test/SignClientFixture.cs index 37b2cea..594f397 100644 --- a/Tests/WalletConnectSharp.Sign.Test/SignClientFixture.cs +++ b/Tests/WalletConnectSharp.Sign.Test/SignClientFixture.cs @@ -9,8 +9,10 @@ public class SignClientFixture : TwoClientsFixture { public SignClientOptions OptionsA { get; protected set; } public SignClientOptions OptionsB { get; protected set; } - - public SignClientFixture(bool initNow = true) : base(initNow) + + public SignClientFixture() : this(true) { } + + internal SignClientFixture(bool initNow) : base(initNow) { } diff --git a/Tests/WalletConnectSharp.Sign.Test/WalletConnectSharp.Sign.Test.csproj b/Tests/WalletConnectSharp.Sign.Test/WalletConnectSharp.Sign.Test.csproj index f0f04a4..488bd90 100644 --- a/Tests/WalletConnectSharp.Sign.Test/WalletConnectSharp.Sign.Test.csproj +++ b/Tests/WalletConnectSharp.Sign.Test/WalletConnectSharp.Sign.Test.csproj @@ -21,6 +21,7 @@ + diff --git a/WalletConnectSharp.Core/Controllers/Relayer.cs b/WalletConnectSharp.Core/Controllers/Relayer.cs index 06496de..6ce7e82 100644 --- a/WalletConnectSharp.Core/Controllers/Relayer.cs +++ b/WalletConnectSharp.Core/Controllers/Relayer.cs @@ -9,7 +9,6 @@ using WalletConnectSharp.Events.Model; using WalletConnectSharp.Network; using WalletConnectSharp.Network.Models; -using WalletConnectSharp.Network.Websocket; namespace WalletConnectSharp.Core.Controllers { @@ -176,7 +175,7 @@ protected virtual async Task CreateProvider() protected virtual IJsonRpcProvider CreateProvider(string auth) { return new JsonRpcProvider( - new WebsocketConnection( + BuildConnection( RelayUrl.FormatRelayRpcUrl( relayUrl, IRelayer.Protocol, @@ -189,6 +188,12 @@ protected virtual IJsonRpcProvider CreateProvider(string auth) ); } + protected virtual IJsonRpcConnection BuildConnection(string url) + { + return Core.Options.ConnectionBuilder.CreateConnection(url); + //return new WebsocketConnection(url); + } + protected virtual void RegisterProviderEventListeners() { Provider.On(ProviderEvents.RawRequestMessage, (sender, @event) => diff --git a/WalletConnectSharp.Core/Interfaces/ICore.cs b/WalletConnectSharp.Core/Interfaces/ICore.cs index 0471594..f23fb61 100644 --- a/WalletConnectSharp.Core/Interfaces/ICore.cs +++ b/WalletConnectSharp.Core/Interfaces/ICore.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using WalletConnectSharp.Common; +using WalletConnectSharp.Core.Models; using WalletConnectSharp.Core.Models.Verify; using WalletConnectSharp.Crypto.Interfaces; using WalletConnectSharp.Events.Interfaces; @@ -32,6 +33,11 @@ public interface ICore : IModule, IEvents /// public string ProjectId { get; } + /// + /// The CoreOptions this Core module was initialized with + /// + public CoreOptions Options { get; } + //TODO Add logger /// diff --git a/WalletConnectSharp.Core/Metadata.cs b/WalletConnectSharp.Core/Metadata.cs index b67009f..542da7a 100644 --- a/WalletConnectSharp.Core/Metadata.cs +++ b/WalletConnectSharp.Core/Metadata.cs @@ -7,36 +7,31 @@ namespace WalletConnectSharp.Core /// A class that holds Metadata for either peer in a given Session. Includes things such /// as Name of peer, Description, urls and images. /// + [Serializable] public class Metadata { /// /// The name of this peer /// - [JsonProperty("name")] - public string Name { get; set; } - + [JsonProperty("name")] public string Name; + /// /// The description for this peer /// - [JsonProperty("description")] - public string Description { get; set; } - + [JsonProperty("description")] public string Description; + /// /// The URL of the software this peer represents /// - [JsonProperty("url")] - public string Url { get; set; } - + [JsonProperty("url")] public string Url; + /// /// The URL of image icons of the software this peer represents /// - [JsonProperty("icons")] - public string[] Icons { get; set; } - - [JsonProperty("redirect")] - public RedirectData Redirect { get; set; } - - [JsonProperty("verifyUrl")] - public string VerifyUrl { get; set; } + [JsonProperty("icons")] public string[] Icons; + + [JsonProperty("redirect")] public RedirectData Redirect; + + [JsonProperty("verifyUrl")] public string VerifyUrl; } } diff --git a/WalletConnectSharp.Core/Models/CoreOptions.cs b/WalletConnectSharp.Core/Models/CoreOptions.cs index 10e2d92..138abe5 100644 --- a/WalletConnectSharp.Core/Models/CoreOptions.cs +++ b/WalletConnectSharp.Core/Models/CoreOptions.cs @@ -1,5 +1,7 @@ using Newtonsoft.Json; +using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Crypto.Interfaces; +using WalletConnectSharp.Network.Interfaces; using WalletConnectSharp.Storage; using WalletConnectSharp.Storage.Interfaces; @@ -50,5 +52,12 @@ public class CoreOptions [JsonProperty("keychain")] public IKeyChain KeyChain { get; set; } + /// + /// The interface to use inside the Relayer to build + /// new websocket connections. + /// + [JsonProperty("connectionBuilder")] + public IConnectionBuilder ConnectionBuilder { get; set; } + } } diff --git a/WalletConnectSharp.Core/Models/RedirectData.cs b/WalletConnectSharp.Core/Models/RedirectData.cs index f7e4c8d..248abbd 100644 --- a/WalletConnectSharp.Core/Models/RedirectData.cs +++ b/WalletConnectSharp.Core/Models/RedirectData.cs @@ -2,10 +2,11 @@ namespace WalletConnectSharp.Core.Models { + [Serializable] public class RedirectData { - [JsonProperty("native")] public string Native { get; set; } + [JsonProperty("native")] public string Native; - [JsonProperty("universal")] public string Universal { get; set; } + [JsonProperty("universal")] public string Universal; } } diff --git a/WalletConnectSharp.Core/WalletConnectCore.cs b/WalletConnectSharp.Core/WalletConnectCore.cs index 02f75a2..c5b5228 100644 --- a/WalletConnectSharp.Core/WalletConnectCore.cs +++ b/WalletConnectSharp.Core/WalletConnectCore.cs @@ -8,6 +8,7 @@ using WalletConnectSharp.Events; using WalletConnectSharp.Storage; using WalletConnectSharp.Storage.Interfaces; +using WalletConnectSharp.Network.Websocket; namespace WalletConnectSharp.Core { @@ -111,6 +112,8 @@ public string Context public IPairing Pairing { get; } public Verifier Verify { get; } + + public CoreOptions Options { get; } /// /// Create a new Core with the given options. @@ -139,7 +142,10 @@ public WalletConnectCore(CoreOptions options = null) { options.KeyChain = new KeyChain(options.Storage); } + + options.ConnectionBuilder ??= new WebsocketConnectionBuilder(); + Options = options; ProjectId = options.ProjectId; RelayUrl = options.RelayUrl; Crypto = new Crypto.Crypto(options.KeyChain); diff --git a/WalletConnectSharp.Core/WalletConnectSharp.Core.csproj b/WalletConnectSharp.Core/WalletConnectSharp.Core.csproj index 59697c5..bc8d956 100644 --- a/WalletConnectSharp.Core/WalletConnectSharp.Core.csproj +++ b/WalletConnectSharp.Core/WalletConnectSharp.Core.csproj @@ -22,6 +22,7 @@ + diff --git a/WalletConnectSharp.Sign/Engine.cs b/WalletConnectSharp.Sign/Engine.cs index eb185c5..25b6a98 100644 --- a/WalletConnectSharp.Sign/Engine.cs +++ b/WalletConnectSharp.Sign/Engine.cs @@ -454,7 +454,7 @@ public async Task Reject(RejectParams @params) /// The topic to update /// The updated namespaces /// A task that returns an interface that can be used to listen for acknowledgement of the updates - public async Task Update(string topic, Namespaces namespaces) + public async Task UpdateSession(string topic, Namespaces namespaces) { IsInitialized(); await PrivateThis.IsValidUpdate(topic, namespaces); diff --git a/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs b/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs index 2d4fb4e..21f6746 100644 --- a/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs +++ b/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs @@ -86,7 +86,7 @@ public interface IEngineAPI /// The topic to update /// The updated namespaces /// A task that returns an interface that can be used to listen for acknowledgement of the updates - Task Update(string topic, Namespaces namespaces); + Task UpdateSession(string topic, Namespaces namespaces); /// /// Extend a session in the given topic. diff --git a/WalletConnectSharp.Sign/Models/Engine/ConnectOptions.cs b/WalletConnectSharp.Sign/Models/Engine/ConnectOptions.cs index 01a3102..7b28583 100644 --- a/WalletConnectSharp.Sign/Models/Engine/ConnectOptions.cs +++ b/WalletConnectSharp.Sign/Models/Engine/ConnectOptions.cs @@ -24,7 +24,7 @@ public class ConnectOptions /// /// Custom session properties for this session /// - [JsonProperty("sessionProperties")] + [JsonProperty("sessionProperties", NullValueHandling = NullValueHandling.Ignore)] public Dictionary SessionProperties { get; set; } /// @@ -48,7 +48,7 @@ public class ConnectOptions public ConnectOptions() { RequiredNamespaces = new RequiredNamespaces(); - SessionProperties = new Dictionary(); + SessionProperties = null; OptionalNamespaces = new Dictionary(); } @@ -63,7 +63,7 @@ public ConnectOptions(RequiredNamespaces requiredNamespaces = null, string pairi { RequiredNamespaces = requiredNamespaces ?? new RequiredNamespaces(); OptionalNamespaces = optionalNamespaces ?? new Dictionary(); - SessionProperties = sessionProperties ?? new Dictionary(); + SessionProperties = sessionProperties ?? null; PairingTopic = pairingTopic ?? ""; Relays = relays; } @@ -102,6 +102,8 @@ public ConnectOptions WithOptionalNamespace(string chain, ProposedNamespace prop /// This object, acts a builder function public ConnectOptions AddSessionProperty(string key, string value) { + SessionProperties ??= new Dictionary(); + SessionProperties.Add(key, value); return this; diff --git a/WalletConnectSharp.Sign/Models/SignClientOptions.cs b/WalletConnectSharp.Sign/Models/SignClientOptions.cs index e4ea84c..3899d46 100644 --- a/WalletConnectSharp.Sign/Models/SignClientOptions.cs +++ b/WalletConnectSharp.Sign/Models/SignClientOptions.cs @@ -7,20 +7,20 @@ namespace WalletConnectSharp.Sign.Models { /// - /// Options for setting up the class. Includes + /// Options for setting up the class. Includes /// options from /// public class SignClientOptions : CoreOptions { /// - /// The instance the should use. If + /// The instance the should use. If /// left null, then a new Core module will be created and initialized /// [JsonProperty("core")] public ICore Core { get; set; } /// - /// The Metadata the should broadcast with + /// The Metadata the should broadcast with /// [JsonProperty("metadata")] public Metadata Metadata { get; set; } diff --git a/WalletConnectSharp.Sign/WalletConnectSignClient.cs b/WalletConnectSharp.Sign/WalletConnectSignClient.cs index e31ce51..539bb4d 100644 --- a/WalletConnectSharp.Sign/WalletConnectSignClient.cs +++ b/WalletConnectSharp.Sign/WalletConnectSignClient.cs @@ -2,8 +2,6 @@ using WalletConnectSharp.Core; using WalletConnectSharp.Core.Controllers; using WalletConnectSharp.Core.Interfaces; -using WalletConnectSharp.Core.Models; -using WalletConnectSharp.Core.Models.Pairing; using WalletConnectSharp.Core.Models.Relay; using WalletConnectSharp.Crypto; using WalletConnectSharp.Events; @@ -300,9 +298,9 @@ public Task Reject(ProposalStruct proposalStruct, Error error) /// The topic to update /// The updated namespaces /// A task that returns an interface that can be used to listen for acknowledgement of the updates - public Task Update(string topic, Namespaces namespaces) + public Task UpdateSession(string topic, Namespaces namespaces) { - return Engine.Update(topic, namespaces); + return Engine.UpdateSession(topic, namespaces); } /// diff --git a/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs b/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs index ecec6a4..57c65ba 100644 --- a/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs +++ b/WalletConnectSharp.Web3Wallet/Controllers/Web3WalletEngine.cs @@ -120,7 +120,7 @@ public Task RejectSession(ProposalStruct proposal, string reason) public async Task UpdateSession(string topic, Namespaces namespaces) { - await (await this.SignClient.Update(topic, namespaces)).Acknowledged(); + await (await this.SignClient.UpdateSession(topic, namespaces)).Acknowledged(); } public async Task ExtendSession(string topic) From 25abbba27853f730c35d77bc20badf61160db11a Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Wed, 19 Jul 2023 23:17:39 -0400 Subject: [PATCH 19/36] fix: #77 --- WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs b/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs index a468574..12f3df6 100644 --- a/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs +++ b/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs @@ -32,7 +32,7 @@ public string Name { get { - return $"{_core.Name}-history-of-type-{typeof(T).Name}"; + return $"{_core.Name}-history-of-type-{typeof(T).FullName}-{typeof(TR).FullName}"; } } From a35560fd7925158731699e2055c97526ee63ec09 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Wed, 19 Jul 2023 23:18:01 -0400 Subject: [PATCH 20/36] chore: reduce default client test count --- Tests/WalletConnectSharp.Tests.Common/TestValues.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/WalletConnectSharp.Tests.Common/TestValues.cs b/Tests/WalletConnectSharp.Tests.Common/TestValues.cs index ab4dc14..543aabf 100644 --- a/Tests/WalletConnectSharp.Tests.Common/TestValues.cs +++ b/Tests/WalletConnectSharp.Tests.Common/TestValues.cs @@ -17,7 +17,7 @@ public static class TestValues private static readonly string EnvironmentClientCount = Environment.GetEnvironmentVariable("CLIENTS"); public static readonly int ClientCount = !string.IsNullOrWhiteSpace(EnvironmentClientCount) ? int.Parse(EnvironmentClientCount) - : 300000; + : 200; private static readonly string EnvironmentMessageCount = Environment.GetEnvironmentVariable("MESSAGES_PER_CLIENT"); public static readonly int MessagesPerClient = !string.IsNullOrWhiteSpace(EnvironmentMessageCount) From e9768cff320f1048311f11c8dd2b703fa288b69a Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Wed, 19 Jul 2023 23:18:26 -0400 Subject: [PATCH 21/36] fix: assert in sign test --- Tests/WalletConnectSharp.Sign.Test/SignTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/WalletConnectSharp.Sign.Test/SignTests.cs b/Tests/WalletConnectSharp.Sign.Test/SignTests.cs index 11395b2..d13ffa3 100644 --- a/Tests/WalletConnectSharp.Sign.Test/SignTests.cs +++ b/Tests/WalletConnectSharp.Sign.Test/SignTests.cs @@ -87,7 +87,7 @@ public static async Task TestConnectMethod(ISignClient clientA, I Assert.NotNull(proposal.RequiredNamespaces); Assert.NotNull(proposal.OptionalNamespaces); - Assert.NotNull(proposal.SessionProperties); + Assert.True(proposal.SessionProperties == null || proposal.SessionProperties.Count > 0); Assert.NotNull(proposal.Expiry); Assert.NotNull(proposal.Id); Assert.NotNull(proposal.Relays); From 840cf451d9a53588ad2f29c924033adc16daef96 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Wed, 19 Jul 2023 23:19:18 -0400 Subject: [PATCH 22/36] fix: batch size in sign client test --- Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs b/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs index 543cb17..a707379 100644 --- a/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs +++ b/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs @@ -193,7 +193,7 @@ async void SendMessages() } int[] arr = Enumerable.Range(0, TestValues.ClientCount+1).ToArray(); - int[][] batches = BatchArray(arr, 100); + int[][] batches = BatchArray(arr, 20); async Task ConnectClient() { From 3f01b58ad9b30e30b429717a1111a21b9ca56fd9 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Thu, 20 Jul 2023 01:49:09 -0400 Subject: [PATCH 23/36] fix: #77 routing --- .../Controllers/JsonRpcHistory.cs | 17 ++++++++++---- .../Controllers/TypedMessageHandler.cs | 23 +++++++++++++++---- .../Models/SessionRequestEventHandler.cs | 19 ++++++++++----- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs b/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs index 12f3df6..552954c 100644 --- a/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs +++ b/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs @@ -237,11 +237,20 @@ public void Delete(string topic, long? id) /// 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); - var record = GetRecord(id); + try + { + IsInitialized(); + if (_records.ContainsKey(id)) return Task.FromResult(false); + var record = GetRecord(id); - return Task.FromResult(record.Topic == topic); + return Task.FromResult(record.Topic == topic); + } + catch (WalletConnectException e) + { + if (e.CodeType == ErrorType.NO_MATCHING_KEY) + return Task.FromResult(false); + throw; + } } private Task SetJsonRpcRecords(JsonRpcRecord[] records) diff --git a/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs b/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs index 01332ab..46675bb 100644 --- a/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs +++ b/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs @@ -1,4 +1,5 @@ -using WalletConnectSharp.Common.Model.Errors; +using Newtonsoft.Json; +using WalletConnectSharp.Common.Model.Errors; using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Core.Models.Relay; using WalletConnectSharp.Crypto.Models; @@ -117,11 +118,25 @@ async void ResponseCallback(object sender, GenericEvent e) var options = DecodeOptionForTopic(topic); - var payload = await this.Core.Crypto.Decode>(topic, message, options); + var rawResultPayload = await this.Core.Crypto.Decode(topic, message, options); - await (await this.Core.History.JsonRpcHistoryOfType()).Resolve(payload); + var history = await this.Core.History.JsonRpcHistoryOfType(); + var expectingResult = await history.Exists(topic, rawResultPayload.Id); - await responseCallback(topic, payload); + try + { + var payload = await this.Core.Crypto.Decode>(topic, message, options); + + await history.Resolve(payload); + + await responseCallback(topic, payload); + } + catch (Exception ex) when (ex is JsonReaderException or JsonSerializationException) + { + if (!expectingResult) + return; + throw; + } } async void InspectResponseRaw(object sender, GenericEvent e) diff --git a/WalletConnectSharp.Sign/Models/SessionRequestEventHandler.cs b/WalletConnectSharp.Sign/Models/SessionRequestEventHandler.cs index 2cab77a..7fa28fa 100644 --- a/WalletConnectSharp.Sign/Models/SessionRequestEventHandler.cs +++ b/WalletConnectSharp.Sign/Models/SessionRequestEventHandler.cs @@ -66,8 +66,15 @@ private Task WrappedRefOnOnResponse(ResponseEventArgs e) private async Task WrappedRefOnOnRequest(RequestEventArgs, TR> e) { + // Ensure that the request is for us + var method = RpcMethodAttribute.MethodForType(); + + var sessionRequest = e.Request.Params.Request; + + if (sessionRequest.Method != method) return; + //Set inner request id to match outer request id - e.Request.Params.Request.Id = e.Request.Id; + sessionRequest.Id = e.Request.Id; //Add to pending requests //We can't do a simple cast, so we need to copy all the data @@ -78,14 +85,14 @@ await _enginePrivate.SetPendingSessionRequest(new PendingRequestStruct() ChainId = e.Request.Params.ChainId, Request = new JsonRpcRequest() { - Id = e.Request.Params.Request.Id, - Method = e.Request.Params.Request.Method, - Params = e.Request.Params.Request.Params + Id = sessionRequest.Id, + Method = sessionRequest.Method, + Params = sessionRequest.Params } }, Topic = e.Topic }); - - await base.RequestCallback(e.Topic, e.Request.Params.Request); + + await base.RequestCallback(e.Topic, sessionRequest); } } } From d96fd2c97fcb69cd228aa64c908932458619b64b Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Thu, 20 Jul 2023 01:49:21 -0400 Subject: [PATCH 24/36] test: #77 --- .../WalletConnectSharp.Sign.Test/SignTests.cs | 128 ++++++++++++++++++ .../UtilExtensions.cs | 37 +++++ 2 files changed, 165 insertions(+) create mode 100644 Tests/WalletConnectSharp.Tests.Common/UtilExtensions.cs diff --git a/Tests/WalletConnectSharp.Sign.Test/SignTests.cs b/Tests/WalletConnectSharp.Sign.Test/SignTests.cs index d13ffa3..f739761 100644 --- a/Tests/WalletConnectSharp.Sign.Test/SignTests.cs +++ b/Tests/WalletConnectSharp.Sign.Test/SignTests.cs @@ -4,6 +4,7 @@ using WalletConnectSharp.Sign.Interfaces; using WalletConnectSharp.Sign.Models; using WalletConnectSharp.Sign.Models.Engine; +using WalletConnectSharp.Tests.Common; using Xunit; namespace WalletConnectSharp.Sign.Test @@ -11,6 +12,7 @@ namespace WalletConnectSharp.Sign.Test public class SignTests : IClassFixture { private SignClientFixture _cryptoFixture; + private const string AllowedChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; [RpcMethod("test_method"), RpcRequestOptions(Clock.ONE_MINUTE, 99998)] public class TestRequest @@ -18,6 +20,15 @@ public class TestRequest public int a; public int b; } + + [RpcMethod("test_method_2"), + RpcRequestOptions(Clock.ONE_MINUTE, 99997), + RpcResponseOptions(Clock.ONE_MINUTE, 99996)] + public class TestRequest2 + { + public string x; + public int y; + } [RpcResponseOptions(Clock.ONE_MINUTE, 99999)] public class TestResponse @@ -254,5 +265,122 @@ public async void TestSessionRequestResponse() Assert.Equal(eventResult, testData.a * testData.b); Assert.Equal(eventResult, responseReturned.result); } + + [Fact, Trait("Category", "integration")] + public async void TestTwoUniqueSessionRequestResponse() + { + await _cryptoFixture.WaitForClientsReady(); + + var testAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"; + var testMethod = "test_method"; + var testMethod2 = "test_method_2"; + + var dappConnectOptions = new ConnectOptions() + { + RequiredNamespaces = new RequiredNamespaces() + { + { + "eip155", new ProposedNamespace() + { + Methods = new[] + { + testMethod, + testMethod2 + }, + Chains = new[] + { + "eip155:1" + }, + 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, testAddress); + + var sessionData = await connectData.Approval; + await approveData.Acknowledged(); + + var rnd = new Random(); + var a = rnd.Next(100); + var b = rnd.Next(100); + var x = rnd.NextStrings(AllowedChars, (Math.Min(a, b), Math.Max(a, b)), 1).First(); + var y = x.Length; + + var testData = new TestRequest() { a = a, b = b, }; + var testData2 = new TestRequest2() { x = x, y = y }; + + var pending = new TaskCompletionSource(); + var pending2 = new TaskCompletionSource(); + + // Step 1. Setup event listener for request + + // The wallet client will listen for the request with the "test_method" rpc method + walletClient.Engine.SessionRequestEvents() + .OnRequest += ( requestData) => + { + var request = requestData.Request; + var data = request.Params; + + requestData.Response = new TestResponse() + { + result = data.a * data.b + }; + + return Task.CompletedTask; + }; + + // The wallet client will listen for the request with the "test_method" rpc method + walletClient.Engine.SessionRequestEvents() + .OnRequest += ( requestData) => + { + var request = requestData.Request; + var data = request.Params; + + requestData.Response = data.x.Length == data.y; + + return Task.CompletedTask; + }; + + // The dapp client will listen for the response + // Normally, we wouldn't do this and just rely on the return value + // from the dappClient.Engine.Request function call (the response Result or throws an Exception) + // We do it here for the sake of testing + dappClient.Engine.SessionRequestEvents() + .FilterResponses((r) => r.Topic == sessionData.Topic) + .OnResponse += (responseData) => + { + var response = responseData.Response; + + var data = response.Result; + + pending.TrySetResult(data.result); + + return Task.CompletedTask; + }; + + // 2. Send the request from the dapp client + var responseReturned = await dappClient.Engine.Request(sessionData.Topic, testData); + var responseReturned2 = await dappClient.Engine.Request(sessionData.Topic, testData2); + + // 3. Wait for the response from the event listener + var eventResult = await pending.Task.WithTimeout(TimeSpan.FromSeconds(5)); + + Assert.Equal(eventResult, a * b); + Assert.Equal(eventResult, testData.a * testData.b); + Assert.Equal(eventResult, responseReturned.result); + + Assert.True(responseReturned2); + } } } diff --git a/Tests/WalletConnectSharp.Tests.Common/UtilExtensions.cs b/Tests/WalletConnectSharp.Tests.Common/UtilExtensions.cs new file mode 100644 index 0000000..96a17f1 --- /dev/null +++ b/Tests/WalletConnectSharp.Tests.Common/UtilExtensions.cs @@ -0,0 +1,37 @@ +namespace WalletConnectSharp.Tests.Common; + +public static class UtilExtensions +{ + public static IEnumerable NextStrings( + this Random rnd, + string allowedChars, + (int Min, int Max)length, + int count) + { + ISet usedRandomStrings = new HashSet(); + (int min, int max) = length; + char[] chars = new char[max]; + int setLength = allowedChars.Length; + + while (count-- > 0) + { + int stringLength = rnd.Next(min, max + 1); + + for (int i = 0; i < stringLength; ++i) + { + chars[i] = allowedChars[rnd.Next(setLength)]; + } + + string randomString = new string(chars, 0, stringLength); + + if (usedRandomStrings.Add(randomString)) + { + yield return randomString; + } + else + { + count++; + } + } + } +} From 7e9c765623e244f9311376abadace3649fee3631 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Thu, 20 Jul 2023 01:53:19 -0400 Subject: [PATCH 25/36] fix: format message in auth engine --- WalletConnectSharp.Auth/Controllers/AuthEngine.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs index 52b301e..c4b206a 100644 --- a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs +++ b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs @@ -367,8 +367,8 @@ public string FormatMessage(Cacao.CacaoPayload cacao) { string iss = cacao.Iss; var header = $"{cacao.Domain} wants you to sign in with your Ethereum account:"; - var walletAddress = IssDidUtils.DidAddress(iss); - var statement = cacao.Statement; + var walletAddress = IssDidUtils.DidAddress(iss) + "\n" + (cacao.Statement != null ? "" : "\n"); + var statement = cacao.Statement + "\n"; var uri = $"URI: {cacao.Aud}"; var version = $"Version: {cacao.Version}"; var chainId = $"Chain ID: {IssDidUtils.DidChainId(iss)}"; @@ -379,7 +379,7 @@ public string FormatMessage(Cacao.CacaoPayload cacao) : null; var message = string.Join('\n', - new string[] { header, walletAddress, "", statement, "", uri, version, chainId, nonce, issuedAt, resources } + new string[] { header, walletAddress, statement, uri, version, chainId, nonce, issuedAt, resources } .Where(val => !string.IsNullOrWhiteSpace(val))); return message; From 752735c80ff96e21e7201edb0f4132fe5e0e27b8 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Thu, 20 Jul 2023 01:56:36 -0400 Subject: [PATCH 26/36] fix: replace cacao header type with literal string --- WalletConnectSharp.Auth/Controllers/AuthEngine.cs | 6 +++--- WalletConnectSharp.Auth/Internals/AuthEngineValidations.cs | 2 +- WalletConnectSharp.Auth/Models/AuthPayload.cs | 2 +- WalletConnectSharp.Sign/Engine.cs | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs index c4b206a..2fcdb53 100644 --- a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs +++ b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs @@ -73,7 +73,7 @@ public async Task Request(RequestParams @params, string topic = null { IsInitialized(); - @params.Type ??= new Cacao.CacaoHeader(); + @params.Type ??= "eip4361"; if (!IsValidRequest(@params)) { @@ -112,7 +112,7 @@ await this.Client.PairingTopics.Set(responseTopic, { Payload = new PayloadParams() { - Type = @params.Type ?? new Cacao.CacaoHeader(), + Type = @params.Type ?? "eip4361", ChainId = @params.ChainId, Statement = @params.Statement, Aud = @params.Aud, @@ -140,7 +140,7 @@ private async Task RequestOnKnownPairing(string topic, RequestParams { Payload = new PayloadParams() { - Type = @params.Type ?? new Cacao.CacaoHeader(), + Type = @params.Type ?? "eip4361", ChainId = @params.ChainId, Statement = @params.Statement, Aud = @params.Aud, diff --git a/WalletConnectSharp.Auth/Internals/AuthEngineValidations.cs b/WalletConnectSharp.Auth/Internals/AuthEngineValidations.cs index 93f7a07..9210c71 100644 --- a/WalletConnectSharp.Auth/Internals/AuthEngineValidations.cs +++ b/WalletConnectSharp.Auth/Internals/AuthEngineValidations.cs @@ -20,7 +20,7 @@ internal bool IsValidRequest(RequestParams @params) // const validChainId = isValidChainId(params.chainId); var domainInAud = @params.Aud.Contains(@params.Domain); var hasNonce = !string.IsNullOrWhiteSpace(@params.Nonce); - var hasValidType = @params.Type is { t: "eip4361" }; + var hasValidType = @params.Type == "eip4361"; var expiry = @params.Expiry; if (expiry != null && !Utils.IsValidRequestExpiry(expiry.Value, MinExpiry, MaxExpiry)) { diff --git a/WalletConnectSharp.Auth/Models/AuthPayload.cs b/WalletConnectSharp.Auth/Models/AuthPayload.cs index 9ef7d26..96d1c07 100644 --- a/WalletConnectSharp.Auth/Models/AuthPayload.cs +++ b/WalletConnectSharp.Auth/Models/AuthPayload.cs @@ -5,7 +5,7 @@ namespace WalletConnectSharp.Auth.Models; public class AuthPayload { [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)] - public Cacao.CacaoHeader? Type { get; set; } + public string? Type { get; set; } [JsonProperty("chainId")] public string ChainId { get; set; } diff --git a/WalletConnectSharp.Sign/Engine.cs b/WalletConnectSharp.Sign/Engine.cs index 25b6a98..91dfb66 100644 --- a/WalletConnectSharp.Sign/Engine.cs +++ b/WalletConnectSharp.Sign/Engine.cs @@ -553,9 +553,9 @@ public async Task Request(string topic, T data, string chainId = null .OnResponse += args => { if (args.Response.IsError) - taskSource.SetException(args.Response.Error.ToException()); + taskSource.TrySetException(args.Response.Error.ToException()); else - taskSource.SetResult(args.Response.Result); + taskSource.TrySetResult(args.Response.Result); return Task.CompletedTask; }; From 523af87998c6b11c0987b9bfa7470f3cc1d474da Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Thu, 27 Jul 2023 00:18:27 -0400 Subject: [PATCH 27/36] fix: null pointer error --- .../WebsocketConnection.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnection.cs b/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnection.cs index 21e3d2d..ea819b7 100644 --- a/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnection.cs +++ b/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnection.cs @@ -180,6 +180,9 @@ private async Task Register(string url) private void OnOpen(WebsocketClient socket) { + if (socket == null) + return; + socket.MessageReceived.Subscribe(OnPayload); socket.DisconnectionHappened.Subscribe(OnDisconnect); From 7eb137d394f4489ad1972847d35573456e8a6cdc Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Thu, 17 Aug 2023 04:14:19 -0400 Subject: [PATCH 28/36] fix: unity il2cpp issues + add logger API --- .../Logging/ILogger.cs | 11 ++++ .../Logging/WCLogger.cs | 31 ++++++++++ .../Models/DecodeOptions.cs | 2 +- .../Models/EncodeOptions.cs | 6 +- .../Models/EncodingParams.cs | 8 +-- .../Models/EncodingValidation.cs | 6 +- .../Models/EncryptParams.cs | 10 ++-- .../Models/IridiumJWTData.cs | 4 +- .../Models/IridiumJWTDecoded.cs | 2 +- .../Models/IridiumJWTHeader.cs | 11 ++-- .../Models/IridiumJWTPayload.cs | 24 ++++---- .../Models/IridiumJWTSigned.cs | 2 +- .../EventDelegator.cs | 7 ++- .../WebsocketConnectionBuilder.cs | 4 +- .../Interfaces/IConnectionBuilder.cs | 2 +- .../Interfaces/IJsonRpcError.cs | 3 +- .../Interfaces/IJsonRpcPayload.cs | 4 +- .../Interfaces/IJsonRpcResult.cs | 3 +- .../Interfaces/IRequestArguments.cs | 2 - .../JsonRpcProvider.cs | 19 ++++++- .../Models/JsonRpcError.cs | 24 +++++--- .../Models/JsonRpcPayload.cs | 18 +++++- .../Models/JsonRpcRequest.cs | 56 +++++++++++++------ .../Models/JsonRpcResponse.cs | 53 ++++++++++++------ .../Models/RequestArguments.cs | 24 +++++++- .../Models/TopicData.cs | 4 +- .../Models/TopicData.cs | 4 +- .../SignClientConcurrency.cs | 4 +- .../SignTests.cs | 2 +- WalletConnectSharp.Auth/Cacao.cs | 6 +- WalletConnectSharp.Auth/Models/AuthData.cs | 4 +- .../Models/AuthErrorResponse.cs | 2 +- WalletConnectSharp.Auth/Models/AuthOptions.cs | 2 +- WalletConnectSharp.Auth/Models/AuthPayload.cs | 20 +++---- WalletConnectSharp.Auth/Models/AuthRequest.cs | 2 +- .../Models/AuthRequestData.cs | 2 +- .../Models/AuthResponse.cs | 2 +- .../Models/ErrorResponse.cs | 2 +- WalletConnectSharp.Auth/Models/Message.cs | 2 +- WalletConnectSharp.Auth/Models/PairingData.cs | 2 +- .../Models/PayloadParams.cs | 2 +- .../Models/PendingRequest.cs | 2 +- .../Models/RequestParams.cs | 2 +- WalletConnectSharp.Auth/Models/RequestUri.cs | 2 +- WalletConnectSharp.Auth/Models/Requester.cs | 4 +- .../Models/ResultResponse.cs | 2 +- .../Models/TopicMessage.cs | 2 +- WalletConnectSharp.Auth/WcAuthRequest.cs | 2 +- .../Controllers/Expirer.cs | 3 +- .../Controllers/Relayer.cs | 40 ++++++++----- .../Controllers/TypedMessageHandler.cs | 3 + WalletConnectSharp.Core/Models/CoreOptions.cs | 21 ++++--- WalletConnectSharp.Core/Models/Eth/EthCall.cs | 8 +-- .../Models/Expirer/Expiration.cs | 8 +-- .../Models/Expirer/ExpirerEventArgs.cs | 4 +- .../Models/Expirer/ExpirerTarget.cs | 4 +- .../Models/History/JsonRpcRecord.cs | 6 +- .../Models/History/RequestEvent.cs | 6 +- .../Models/MessageHandler/RequestEventArgs.cs | 6 +- .../Models/Pairing/CreatePairingData.cs | 4 +- .../Models/Pairing/PairingEvent.cs | 4 +- .../Models/Pairing/PairingStruct.cs | 10 ++-- .../Models/Pairing/UriParameters.cs | 10 ++-- .../Models/Publisher/PublishParams.cs | 6 +- .../Models/Relay/DecodedMessageEvent.cs | 2 +- .../Models/Relay/MessageEvent.cs | 4 +- .../Models/Relay/ProtocolOptionHolder.cs | 2 +- .../Models/Relay/ProtocolOptions.cs | 6 +- .../Models/Relay/PublishOptions.cs | 4 +- .../Models/Relay/RelayPublishRequest.cs | 4 +- .../Models/Relay/RelayerOptions.cs | 4 +- .../Models/Relay/UnsubscribeOptions.cs | 2 +- .../Models/Subscriber/ActiveSubscription.cs | 2 +- .../Models/Subscriber/BatchSubscribeParams.cs | 2 +- .../Models/Subscriber/DeletedSubscription.cs | 2 +- .../Subscriber/JsonRpcSubscriberParams.cs | 2 +- .../Subscriber/JsonRpcSubscriptionParams.cs | 4 +- .../Subscriber/JsonRpcUnsubscribeParams.cs | 4 +- .../Models/Subscriber/MessageData.cs | 2 +- .../Models/Subscriber/PendingSubscription.cs | 2 +- .../Models/Verify/VerifiedContext.cs | 2 +- WalletConnectSharp.Core/WalletConnectCore.cs | 22 ++++++-- WalletConnectSharp.Sign/Engine.cs | 10 ++++ .../Models/Engine/ApproveParams.cs | 8 +-- .../Models/Engine/ConnectOptions.cs | 10 ++-- .../Models/Engine/ConnectedData.cs | 4 +- .../Models/Engine/Events/EmitEvent.cs | 2 +- .../Models/Engine/Events/EventData.cs | 7 +-- .../Models/Engine/Events/SessionEvent.cs | 4 +- .../Engine/Events/SessionProposalEvent.cs | 6 +- .../Engine/Events/SessionRequestEvent.cs | 4 +- .../Engine/Events/SessionUpdateEvent.cs | 2 +- .../Models/Engine/Methods/SessionEvent.cs | 6 +- .../Models/Engine/Methods/SessionPropose.cs | 10 ++-- .../Engine/Methods/SessionProposeResponse.cs | 4 +- .../Models/Engine/Methods/SessionRequest.cs | 4 +- .../Models/Engine/Methods/SessionSettle.cs | 8 +-- .../Models/Engine/Methods/SessionUpdate.cs | 2 +- .../Models/Engine/RejectParams.cs | 4 +- WalletConnectSharp.Sign/Models/Namespace.cs | 6 +- WalletConnectSharp.Sign/Models/Participant.cs | 4 +- .../Models/PendingRequestStruct.cs | 6 +- .../Models/ProposalStruct.cs | 16 +++--- .../Models/ProposedNamespace.cs | 4 +- .../Models/SessionStruct.cs | 18 +++--- .../Models/SignClientOptions.cs | 4 +- .../Models/BaseEventArgs.cs | 2 +- .../Models/SessionRequestEventArgs.cs | 2 +- 108 files changed, 478 insertions(+), 308 deletions(-) create mode 100644 Core Modules/WalletConnectSharp.Common/Logging/ILogger.cs create mode 100644 Core Modules/WalletConnectSharp.Common/Logging/WCLogger.cs diff --git a/Core Modules/WalletConnectSharp.Common/Logging/ILogger.cs b/Core Modules/WalletConnectSharp.Common/Logging/ILogger.cs new file mode 100644 index 0000000..c8dee21 --- /dev/null +++ b/Core Modules/WalletConnectSharp.Common/Logging/ILogger.cs @@ -0,0 +1,11 @@ +namespace WalletConnectSharp.Common.Logging +{ + public interface ILogger + { + void Log(string message); + + void LogError(string message); + + void LogError(Exception e); + } +} diff --git a/Core Modules/WalletConnectSharp.Common/Logging/WCLogger.cs b/Core Modules/WalletConnectSharp.Common/Logging/WCLogger.cs new file mode 100644 index 0000000..a5ae071 --- /dev/null +++ b/Core Modules/WalletConnectSharp.Common/Logging/WCLogger.cs @@ -0,0 +1,31 @@ +namespace WalletConnectSharp.Common.Logging +{ + public class WCLogger + { + public static ILogger Logger; + + public static void Log(string message) + { + if (Logger == null) + return; + + Logger.Log(message); + } + + public static void LogError(string message) + { + if (Logger == null) + return; + + Logger.LogError(message); + } + + public static void LogError(Exception e) + { + if (Logger == null) + return; + + Logger.LogError(e); + } + } +} diff --git a/Core Modules/WalletConnectSharp.Crypto/Models/DecodeOptions.cs b/Core Modules/WalletConnectSharp.Crypto/Models/DecodeOptions.cs index b071425..1bcec66 100644 --- a/Core Modules/WalletConnectSharp.Crypto/Models/DecodeOptions.cs +++ b/Core Modules/WalletConnectSharp.Crypto/Models/DecodeOptions.cs @@ -11,6 +11,6 @@ public class DecodeOptions /// The public key that received this encoded message /// [JsonProperty("receiverPublicKey")] - public string ReceiverPublicKey { get; set; } + public string ReceiverPublicKey; } } diff --git a/Core Modules/WalletConnectSharp.Crypto/Models/EncodeOptions.cs b/Core Modules/WalletConnectSharp.Crypto/Models/EncodeOptions.cs index 498663d..3c280c0 100644 --- a/Core Modules/WalletConnectSharp.Crypto/Models/EncodeOptions.cs +++ b/Core Modules/WalletConnectSharp.Crypto/Models/EncodeOptions.cs @@ -11,18 +11,18 @@ public class EncodeOptions /// The envelope type to use /// [JsonProperty("type")] - public int Type { get; set; } + public int Type; /// /// The public key that is sending the encoded message /// [JsonProperty("senderPublicKey")] - public string SenderPublicKey { get; set; } + public string SenderPublicKey; /// /// The public key that is receiving the encoded message /// [JsonProperty("receiverPublicKey")] - public string ReceiverPublicKey { get; set; } + public string ReceiverPublicKey; } } diff --git a/Core Modules/WalletConnectSharp.Crypto/Models/EncodingParams.cs b/Core Modules/WalletConnectSharp.Crypto/Models/EncodingParams.cs index bc28987..2e91cbf 100644 --- a/Core Modules/WalletConnectSharp.Crypto/Models/EncodingParams.cs +++ b/Core Modules/WalletConnectSharp.Crypto/Models/EncodingParams.cs @@ -12,24 +12,24 @@ public class EncodingParams /// The envelope type as raw bytes /// [JsonProperty("type")] - public byte[] Type { get; set; } + public byte[] Type; /// /// The sealed encoded message as raw bytes /// [JsonProperty("sealed")] - public byte[] Sealed { get; set; } + public byte[] Sealed; /// /// The IV of the encoded message as raw bytes /// [JsonProperty("iv")] - public byte[] Iv { get; set; } + public byte[] Iv; /// /// The public key of the sender as raw bytes /// [JsonProperty("senderPublicKey")] - public byte[] SenderPublicKey { get; set; } + public byte[] SenderPublicKey; } } diff --git a/Core Modules/WalletConnectSharp.Crypto/Models/EncodingValidation.cs b/Core Modules/WalletConnectSharp.Crypto/Models/EncodingValidation.cs index c4c4118..b0aa633 100644 --- a/Core Modules/WalletConnectSharp.Crypto/Models/EncodingValidation.cs +++ b/Core Modules/WalletConnectSharp.Crypto/Models/EncodingValidation.cs @@ -11,18 +11,18 @@ public class EncodingValidation /// The envelope type to validate /// [JsonProperty("type")] - public int Type { get; set; } + public int Type; /// /// The sender public key to validate /// [JsonProperty("senderPublicKey")] - public string SenderPublicKey { get; set; } + public string SenderPublicKey; /// /// The receiver public key to validate /// [JsonProperty("receiverPublicKey")] - public string ReceiverPublicKey { get; set; } + public string ReceiverPublicKey; } } diff --git a/Core Modules/WalletConnectSharp.Crypto/Models/EncryptParams.cs b/Core Modules/WalletConnectSharp.Crypto/Models/EncryptParams.cs index 986b971..5cd154f 100644 --- a/Core Modules/WalletConnectSharp.Crypto/Models/EncryptParams.cs +++ b/Core Modules/WalletConnectSharp.Crypto/Models/EncryptParams.cs @@ -11,30 +11,30 @@ public class EncryptParams /// The message to encrypt /// [JsonProperty("message")] - public string Message { get; set; } + public string Message; /// /// The Sym key to use for encrypting /// [JsonProperty("symKey")] - public string SymKey { get; set; } + public string SymKey; /// /// The envelope type to use when encrypting /// [JsonProperty("type")] - public int Type { get; set; } + public int Type; /// /// The IV to use for the encryption /// [JsonProperty("iv")] - public string Iv { get; set; } + public string Iv; /// /// The public key of the sender of this encrypted message /// [JsonProperty("senderPublicKey")] - public string SenderPublicKey { get; set; } + public string SenderPublicKey; } } diff --git a/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTData.cs b/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTData.cs index 181e412..58345c9 100644 --- a/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTData.cs +++ b/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTData.cs @@ -11,12 +11,12 @@ public class IridiumJWTData /// The Iridium JWT header data /// [JsonProperty("header")] - public IridiumJWTHeader Header { get; set; } + public IridiumJWTHeader Header; /// /// The Iridium JWT payload /// [JsonProperty("payload")] - public IridiumJWTPayload Payload { get; set; } + public IridiumJWTPayload Payload; } } diff --git a/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTDecoded.cs b/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTDecoded.cs index 8908f74..952eb27 100644 --- a/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTDecoded.cs +++ b/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTDecoded.cs @@ -6,6 +6,6 @@ namespace WalletConnectSharp.Crypto.Models public class IridiumJWTDecoded : IridiumJWTSigned { [JsonProperty("data")] - public byte[] Data { get; set; } + public byte[] Data; } } diff --git a/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTHeader.cs b/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTHeader.cs index 42999c6..cc5f1aa 100644 --- a/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTHeader.cs +++ b/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTHeader.cs @@ -5,6 +5,7 @@ namespace WalletConnectSharp.Crypto.Models /// /// The header data for an Iridium JWT header /// + [Serializable] public class IridiumJWTHeader { /// @@ -15,17 +16,15 @@ public class IridiumJWTHeader Alg = "EdDSA", Typ = "JWT" }; - + /// /// The encoding algorithm to use /// - [JsonProperty("alg")] - public string Alg { get; set; } - + [JsonProperty("alg")] public string Alg; + /// /// The encoding type to use /// - [JsonProperty("typ")] - public string Typ { get; set; } + [JsonProperty("typ")] public string Typ; } } diff --git a/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTPayload.cs b/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTPayload.cs index 30149c2..9897b3b 100644 --- a/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTPayload.cs +++ b/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTPayload.cs @@ -5,36 +5,32 @@ namespace WalletConnectSharp.Crypto.Models /// /// The data for an Iridium JWT payload /// + [Serializable] public class IridiumJWTPayload { /// /// The iss value /// - [JsonProperty("iss")] - public string Iss { get; set; } - + [JsonProperty("iss")] public string Iss; + /// /// The sub value /// - [JsonProperty("sub")] - public string Sub { get; set; } - + [JsonProperty("sub")] public string Sub; + /// /// The aud value /// - [JsonProperty("aud")] - public string Aud { get; set; } - + [JsonProperty("aud")] public string Aud; + /// /// The iat value /// - [JsonProperty("iat")] - public long Iat { get; set; } - + [JsonProperty("iat")] public long Iat; + /// /// The exp value /// - [JsonProperty("exp")] - public long Exp { get; set; } + [JsonProperty("exp")] public long Exp; } } diff --git a/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTSigned.cs b/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTSigned.cs index 6b01948..e684da4 100644 --- a/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTSigned.cs +++ b/Core Modules/WalletConnectSharp.Crypto/Models/IridiumJWTSigned.cs @@ -8,6 +8,6 @@ namespace WalletConnectSharp.Crypto.Models public class IridiumJWTSigned : IridiumJWTData { [JsonProperty("signature")] - public byte[] Signature { get; set; } + public byte[] Signature; } } diff --git a/Core Modules/WalletConnectSharp.Events/EventDelegator.cs b/Core Modules/WalletConnectSharp.Events/EventDelegator.cs index a616d4d..da15cfc 100644 --- a/Core Modules/WalletConnectSharp.Events/EventDelegator.cs +++ b/Core Modules/WalletConnectSharp.Events/EventDelegator.cs @@ -4,6 +4,7 @@ using System.Reflection; using Newtonsoft.Json; using WalletConnectSharp.Common; +using WalletConnectSharp.Common.Logging; using WalletConnectSharp.Common.Model; using WalletConnectSharp.Events.Model; @@ -74,7 +75,7 @@ public void ListenFor(string eventId, Action callback) /// The callback to invoke when the event is triggered /// The type of event data the callback MUST be given public void ListenFor(string eventId, EventHandler> callback) - { + { EventManager>.InstanceOf(Context).EventTriggers[eventId] += callback; } @@ -231,8 +232,10 @@ from type in assembly.GetTypes() propagateEventMethod.Invoke(genericProvider, new object[] { eventId, eventData }); wasTriggered = true; } - catch (Exception) + catch (Exception e) { + WCLogger.LogError(e); + WCLogger.Log($"[EventDelegator] Error Invoking EventFactory<{type.FullName}>.Provider.PropagateEvent({eventId}, {eventData})"); if (raiseOnException) throw; } diff --git a/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnectionBuilder.cs b/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnectionBuilder.cs index 6fc818e..7bc1cec 100644 --- a/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnectionBuilder.cs +++ b/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnectionBuilder.cs @@ -4,9 +4,9 @@ namespace WalletConnectSharp.Network.Websocket { public class WebsocketConnectionBuilder : IConnectionBuilder { - public IJsonRpcConnection CreateConnection(string url) + public Task CreateConnection(string url) { - return new WebsocketConnection(url); + return Task.FromResult(new WebsocketConnection(url)); } } } diff --git a/Core Modules/WalletConnectSharp.Network/Interfaces/IConnectionBuilder.cs b/Core Modules/WalletConnectSharp.Network/Interfaces/IConnectionBuilder.cs index 6892cd4..f7a49cd 100644 --- a/Core Modules/WalletConnectSharp.Network/Interfaces/IConnectionBuilder.cs +++ b/Core Modules/WalletConnectSharp.Network/Interfaces/IConnectionBuilder.cs @@ -2,6 +2,6 @@ { public interface IConnectionBuilder { - IJsonRpcConnection CreateConnection(string url); + Task CreateConnection(string url); } } diff --git a/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcError.cs b/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcError.cs index 9791ada..d622751 100644 --- a/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcError.cs +++ b/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcError.cs @@ -11,7 +11,6 @@ public interface IJsonRpcError : IJsonRpcPayload /// /// The error for this JSON RPC response or null if no error is present /// - [JsonProperty("error")] Error Error { get; } } -} \ No newline at end of file +} diff --git a/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcPayload.cs b/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcPayload.cs index a4cc31d..59b7571 100644 --- a/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcPayload.cs +++ b/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcPayload.cs @@ -10,13 +10,11 @@ public interface IJsonRpcPayload /// /// The unique id for this JSON RPC payload /// - [JsonProperty("id")] long Id { get; } /// /// The version of this JSON RPC payload /// - [JsonProperty("jsonrpc")] string JsonRPC { get; } } -} \ No newline at end of file +} diff --git a/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcResult.cs b/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcResult.cs index 9dfeca1..94aacf3 100644 --- a/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcResult.cs +++ b/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcResult.cs @@ -12,7 +12,6 @@ public interface IJsonRpcResult : IJsonRpcError /// /// The result data of the response to the request /// - [JsonProperty("result")] T Result { get; } } -} \ No newline at end of file +} diff --git a/Core Modules/WalletConnectSharp.Network/Interfaces/IRequestArguments.cs b/Core Modules/WalletConnectSharp.Network/Interfaces/IRequestArguments.cs index 562c7ff..a74dfae 100644 --- a/Core Modules/WalletConnectSharp.Network/Interfaces/IRequestArguments.cs +++ b/Core Modules/WalletConnectSharp.Network/Interfaces/IRequestArguments.cs @@ -11,13 +11,11 @@ public interface IRequestArguments /// /// The method for this request /// - [JsonProperty("method")] string Method { get; } /// /// The parameter for this request /// - [JsonProperty("params")] T Params { get; } } } diff --git a/Core Modules/WalletConnectSharp.Network/JsonRpcProvider.cs b/Core Modules/WalletConnectSharp.Network/JsonRpcProvider.cs index 1b6c299..72ba26e 100644 --- a/Core Modules/WalletConnectSharp.Network/JsonRpcProvider.cs +++ b/Core Modules/WalletConnectSharp.Network/JsonRpcProvider.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json; using WalletConnectSharp.Common; +using WalletConnectSharp.Common.Logging; using WalletConnectSharp.Common.Model.Errors; using WalletConnectSharp.Events; using WalletConnectSharp.Events.Model; @@ -106,7 +107,9 @@ public async Task Connect(string connection) Connecting = new TaskCompletionSource(); _connectingStarted = true; + WCLogger.Log("[JsonRpcProvider] Opening connection"); await this._connection.Open(connection); + FinalizeConnection(this._connection); } @@ -126,6 +129,7 @@ public async Task Connect(IJsonRpcConnection connection) Connecting = new TaskCompletionSource(); _connectingStarted = true; + WCLogger.Log("[JsonRpcProvider] Opening connection"); await connection.Open(); FinalizeConnection(connection); @@ -133,6 +137,7 @@ public async Task Connect(IJsonRpcConnection connection) private void FinalizeConnection(IJsonRpcConnection connection) { + WCLogger.Log("[JsonRpcProvider] Finalizing Connection, registering event listeners"); this._connection = connection; RegisterEventListeners(); Events.Trigger(ProviderEvents.Connect, connection); @@ -148,6 +153,8 @@ public async Task Connect() { if (_connection == null) throw new Exception("No connection is set"); + + WCLogger.Log("[JsonRpcProvider] Connecting with given connection object"); await Connect(_connection); } @@ -172,10 +179,12 @@ public async Task Disconnect() /// A Task that will resolve when a response is received public async Task Request(IRequestArguments requestArgs, object context = null) { - if (IsConnecting) + WCLogger.Log("[JsonRpcProvider] Checking if connected"); + if (IsConnecting) await Connecting.Task; else if (!_connectingStarted && !_connection.Connected) { + WCLogger.Log("[JsonRpcProvider] Not connected, connecting now"); await Connect(_connection); } @@ -225,9 +234,11 @@ public async Task Request(IRequestArguments requestArgs, object co _lastId = request.Id; + WCLogger.Log($"[JsonRpcProvider] Sending request {request.Method} with data {JsonConvert.SerializeObject(request)}"); //Console.WriteLine($"[{Name}] Sending request {request.Method} with data {JsonConvert.SerializeObject(request)}"); await _connection.SendRequest(request, context); + WCLogger.Log("[JsonRpcProvider] Awaiting request resuult"); await requestTask.Task; return requestTask.Task.Result; @@ -237,6 +248,7 @@ protected void RegisterEventListeners() { if (_hasRegisteredEventListeners) return; + WCLogger.Log($"[JsonRpcProvider] Registering event listeners on connection object with context {_connection.Events.Context}"); _connection.On("payload", OnPayload); _connection.On("close", OnConnectionDisconnected); _connection.On("error", OnConnectionError); @@ -256,6 +268,8 @@ private void OnConnectionDisconnected(object sender, GenericEvent e) private void OnPayload(object sender, GenericEvent e) { var json = e.EventData; + + WCLogger.Log($"[JsonRpcProvider] Got payload {json}"); var payload = JsonConvert.DeserializeObject(json); @@ -267,6 +281,8 @@ private void OnPayload(object sender, GenericEvent e) if (payload.Id == 0) payload.Id = _lastId; + WCLogger.Log($"[JsonRpcProvider] Payload has ID {payload.Id}"); + Events.Trigger(ProviderEvents.Payload, payload); if (payload.IsRequest) @@ -282,6 +298,7 @@ private void OnPayload(object sender, GenericEvent e) } else { + WCLogger.Log($"Triggering event for ID {payload.Id.ToString()}"); Events.Trigger(payload.Id.ToString(), json); } } diff --git a/Core Modules/WalletConnectSharp.Network/Models/JsonRpcError.cs b/Core Modules/WalletConnectSharp.Network/Models/JsonRpcError.cs index 4d31fd5..52d1abe 100644 --- a/Core Modules/WalletConnectSharp.Network/Models/JsonRpcError.cs +++ b/Core Modules/WalletConnectSharp.Network/Models/JsonRpcError.cs @@ -11,25 +11,35 @@ public class JsonRpcError : IJsonRpcError /// The id field /// [JsonProperty("id")] - public long Id { get; set; } + private long _id; + + [JsonIgnore] + public long Id => _id; + + [JsonProperty("jsonrpc")] + private string _jsonRpc = "2.0"; /// /// The jsonrpc field /// - [JsonProperty("jsonrpc")] + [JsonIgnore] public string JsonRPC { get { - return "2.0"; + return _jsonRpc; } } + + [JsonProperty("error", NullValueHandling = NullValueHandling.Ignore)] + private Error _error; + /// /// The error field /// - [JsonProperty("error", NullValueHandling = NullValueHandling.Ignore)] - public Error Error { get; set; } + [JsonIgnore] + public Error Error => _error; /// /// Create a blank JSON rpc error response @@ -45,8 +55,8 @@ public JsonRpcError() /// The error value public JsonRpcError(long id, Error error) { - Id = id; - Error = error; + _id = id; + _error = error; } } } diff --git a/Core Modules/WalletConnectSharp.Network/Models/JsonRpcPayload.cs b/Core Modules/WalletConnectSharp.Network/Models/JsonRpcPayload.cs index 0cc47fb..7e9e09c 100644 --- a/Core Modules/WalletConnectSharp.Network/Models/JsonRpcPayload.cs +++ b/Core Modules/WalletConnectSharp.Network/Models/JsonRpcPayload.cs @@ -11,15 +11,27 @@ namespace WalletConnectSharp.Network.Models /// public class JsonRpcPayload : IJsonRpcPayload { + [JsonProperty("id")] + private long _id; + + [JsonProperty("jsonrpc")] + private string _jsonRPC = "2.0"; + /// /// The JSON RPC id for this payload /// - public long Id { get; set; } - + [JsonIgnore] + public long Id + { + get => _id; + set => _id = value; + } + /// /// The JSON RPC version for this payload /// - public string JsonRPC { get; set; } + [JsonIgnore] + public string JsonRPC => _jsonRPC; [JsonExtensionData] #pragma warning disable CS0649 diff --git a/Core Modules/WalletConnectSharp.Network/Models/JsonRpcRequest.cs b/Core Modules/WalletConnectSharp.Network/Models/JsonRpcRequest.cs index 428f015..3946884 100644 --- a/Core Modules/WalletConnectSharp.Network/Models/JsonRpcRequest.cs +++ b/Core Modules/WalletConnectSharp.Network/Models/JsonRpcRequest.cs @@ -9,35 +9,59 @@ namespace WalletConnectSharp.Network.Models /// The parameter type for this JSON RPC request public class JsonRpcRequest : IJsonRpcRequest { + [JsonProperty("method")] + private string _method; + /// /// The method of this Json rpc request /// - public string Method { get; set; } - + [JsonIgnore] + public string Method + { + get => _method; + set => _method = value; + } + + [JsonProperty("params")] + private T _params; + /// /// The parameters of this Json rpc request /// - public T Params { get; set; } - + [JsonIgnore] + public T Params + { + get => _params; + set => _params = value; + } + + [JsonProperty("id")] + private long _id; + /// /// The id of this Json rpc request /// - public long Id { get; set; } + [JsonIgnore] + public long Id + { + get => _id; + set => _id = value; + } + [JsonProperty("jsonrpc")] + private string _jsonRPC = "2.0"; + /// - /// The jsonrpc field + /// The JSON RPC version for this payload /// - public string JsonRPC - { - get - { - return "2.0"; - } - } + [JsonIgnore] + public string JsonRPC => _jsonRPC; + /// /// Create a blank Json rpc request /// + [JsonConstructor] public JsonRpcRequest() { } @@ -56,9 +80,9 @@ public JsonRpcRequest(string method, T param, long? id = null) id = RpcPayloadId.Generate(); } - this.Method = method; - this.Params = param; - this.Id = (long)id; + this._method = method; + this._params = param; + this._id = (long)id; } } } diff --git a/Core Modules/WalletConnectSharp.Network/Models/JsonRpcResponse.cs b/Core Modules/WalletConnectSharp.Network/Models/JsonRpcResponse.cs index 951740f..ee55dbc 100644 --- a/Core Modules/WalletConnectSharp.Network/Models/JsonRpcResponse.cs +++ b/Core Modules/WalletConnectSharp.Network/Models/JsonRpcResponse.cs @@ -8,35 +8,51 @@ namespace WalletConnectSharp.Network.Models /// The type of the result property for this JSON RPC response public class JsonRpcResponse : IJsonRpcResult { - /// - /// The id of this Json rpc response, should match the original request - /// + [JsonProperty("id")] - public long Id { get; set; } + private long _id; /// - /// The jsonrpc field + /// The id of this Json rpc response, should match the original request /// - [JsonProperty("jsonrpc")] - public string JsonRPC + [JsonIgnore] + public long Id { - get - { - return "2.0"; - } + get => _id; + set => _id = value; } + + [JsonProperty("jsonrpc")] + private string _jsonRPC = "2.0"; /// - /// The error field for this response, if one is present + /// The JSON RPC version for this payload /// + [JsonIgnore] + public string JsonRPC => _jsonRPC; + [JsonProperty("error", NullValueHandling = NullValueHandling.Ignore)] - public Error Error { get; set; } + private Error _error; + + /// + /// The error field + /// + [JsonIgnore] + public Error Error => _error; + + [JsonProperty("result", NullValueHandling = NullValueHandling.Ignore)] + private T _result; + /// /// The result field for this response, if one is present /// - [JsonProperty("result", NullValueHandling = NullValueHandling.Ignore)] - public T Result { get; set; } + [JsonIgnore] + public T Result + { + get => _result; + set => _result = value; + } /// /// Whether or not this response is an error response @@ -53,6 +69,7 @@ public bool IsError /// /// Create a blank Json rpc response /// + [JsonConstructor] public JsonRpcResponse() { } @@ -66,9 +83,9 @@ public JsonRpcResponse() /// The result of this json response, if one is present public JsonRpcResponse(long id, Error error, T result) { - Id = id; - Error = error; - Result = result; + _id = id; + _error = error; + _result = result; } } } diff --git a/Core Modules/WalletConnectSharp.Network/Models/RequestArguments.cs b/Core Modules/WalletConnectSharp.Network/Models/RequestArguments.cs index 9682dd5..9316779 100644 --- a/Core Modules/WalletConnectSharp.Network/Models/RequestArguments.cs +++ b/Core Modules/WalletConnectSharp.Network/Models/RequestArguments.cs @@ -1,3 +1,5 @@ +using Newtonsoft.Json; + namespace WalletConnectSharp.Network.Models { /// @@ -8,14 +10,30 @@ namespace WalletConnectSharp.Network.Models /// The type of the parameter in this request public class RequestArguments : IRequestArguments { + [JsonProperty("method")] + private string _method; + + [JsonProperty("params")] + private T _params; + /// /// The method to use /// - public string Method { get; set; } - + [JsonIgnore] + public string Method + { + get => _method; + set => _method = value; + } + /// /// The method for this request /// - public T Params { get; set; } + [JsonIgnore] + public T Params + { + get => _params; + set => _params = value; + } } } diff --git a/Tests/WalletConnectSharp.Crypto.Tests/Models/TopicData.cs b/Tests/WalletConnectSharp.Crypto.Tests/Models/TopicData.cs index d5fdf86..240e182 100644 --- a/Tests/WalletConnectSharp.Crypto.Tests/Models/TopicData.cs +++ b/Tests/WalletConnectSharp.Crypto.Tests/Models/TopicData.cs @@ -5,6 +5,6 @@ namespace WalletConnectSharp.Crypto.Tests.Models public class TopicData { [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; } -} \ No newline at end of file +} diff --git a/Tests/WalletConnectSharp.Network.Tests/Models/TopicData.cs b/Tests/WalletConnectSharp.Network.Tests/Models/TopicData.cs index 3001ddf..7856a61 100644 --- a/Tests/WalletConnectSharp.Network.Tests/Models/TopicData.cs +++ b/Tests/WalletConnectSharp.Network.Tests/Models/TopicData.cs @@ -5,6 +5,6 @@ namespace WalletConnectSharp.Network.Tests.Models public class TopicData { [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; } -} \ No newline at end of file +} diff --git a/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs b/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs index a707379..86c848a 100644 --- a/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs +++ b/Tests/WalletConnectSharp.Sign.Test/SignClientConcurrency.cs @@ -34,10 +34,10 @@ public class TestResults public class TestEmitData { [JsonProperty("name")] - public string Name { get; set; } + public string Name; [JsonProperty("data")] - public string Data { get; set; } + public string Data; } private ITestOutputHelper _output; diff --git a/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs b/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs index f6e600e..f1fb099 100644 --- a/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs +++ b/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs @@ -34,7 +34,7 @@ public class EthSignTransaction : List public class ChainChangedEvent { [JsonProperty("test")] - public string Test { get; set; } + public string Test; } private static readonly string TestEthereumAddress = "0x3c582121909DE92Dc89A36898633C1aE4790382b"; diff --git a/WalletConnectSharp.Auth/Cacao.cs b/WalletConnectSharp.Auth/Cacao.cs index b116823..a739a33 100644 --- a/WalletConnectSharp.Auth/Cacao.cs +++ b/WalletConnectSharp.Auth/Cacao.cs @@ -22,13 +22,13 @@ public CacaoHeader() public class CacaoRequestPayload { [JsonProperty("domain")] - public string Domain { get; set; } + public string Domain; [JsonProperty("aud")] - public string Aud { get; set; } + public string Aud; [JsonProperty("version")] - public string Version { get; set; } + public string Version; [JsonProperty("nonce")] public string Nonce { get; set; } diff --git a/WalletConnectSharp.Auth/Models/AuthData.cs b/WalletConnectSharp.Auth/Models/AuthData.cs index a0ad230..6ae0381 100644 --- a/WalletConnectSharp.Auth/Models/AuthData.cs +++ b/WalletConnectSharp.Auth/Models/AuthData.cs @@ -6,10 +6,10 @@ namespace WalletConnectSharp.Auth.Models; public class AuthData : IKeyHolder { [JsonProperty("responseTopic")] - public string ResponseTopic { get; set; } + public string ResponseTopic; [JsonProperty("publicKey")] - public string PublicKey { get; set; } + public string PublicKey; public string Key { diff --git a/WalletConnectSharp.Auth/Models/AuthErrorResponse.cs b/WalletConnectSharp.Auth/Models/AuthErrorResponse.cs index ba614dc..b678c45 100644 --- a/WalletConnectSharp.Auth/Models/AuthErrorResponse.cs +++ b/WalletConnectSharp.Auth/Models/AuthErrorResponse.cs @@ -6,5 +6,5 @@ namespace WalletConnectSharp.Auth.Models; public class AuthErrorResponse : TopicMessage { [JsonProperty("params")] - public Error Error { get; set; } + public Error Error; } diff --git a/WalletConnectSharp.Auth/Models/AuthOptions.cs b/WalletConnectSharp.Auth/Models/AuthOptions.cs index 9c32001..970b04c 100644 --- a/WalletConnectSharp.Auth/Models/AuthOptions.cs +++ b/WalletConnectSharp.Auth/Models/AuthOptions.cs @@ -6,7 +6,7 @@ namespace WalletConnectSharp.Auth.Models; public class AuthOptions : CoreOptions { - public Metadata Metadata { get; set; } + public Metadata Metadata; public ICore Core { get; set; } } diff --git a/WalletConnectSharp.Auth/Models/AuthPayload.cs b/WalletConnectSharp.Auth/Models/AuthPayload.cs index 96d1c07..ad90a89 100644 --- a/WalletConnectSharp.Auth/Models/AuthPayload.cs +++ b/WalletConnectSharp.Auth/Models/AuthPayload.cs @@ -5,32 +5,32 @@ namespace WalletConnectSharp.Auth.Models; public class AuthPayload { [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)] - public string? Type { get; set; } + public string? Type; [JsonProperty("chainId")] - public string ChainId { get; set; } + public string ChainId; [JsonProperty("domain")] - public string Domain { get; set; } + public string Domain; [JsonProperty("aud")] - public string Aud { get; set; } + public string Aud; [JsonProperty("nonce")] - public string Nonce { get; set; } + public string Nonce; [JsonProperty("nbf", NullValueHandling = NullValueHandling.Ignore)] - public string Nbf { get; set; } + public string Nbf; [JsonProperty("exp", NullValueHandling = NullValueHandling.Ignore)] - public string Exp { get; set; } + public string Exp; [JsonProperty("statement", NullValueHandling = NullValueHandling.Ignore)] - public string Statement { get; set; } + public string Statement; [JsonProperty("requestId", NullValueHandling = NullValueHandling.Ignore)] - public string RequestId { get; set; } + public string RequestId; [JsonProperty("resources", NullValueHandling = NullValueHandling.Ignore)] - public string[] Resources { get; set; } + public string[] Resources; } diff --git a/WalletConnectSharp.Auth/Models/AuthRequest.cs b/WalletConnectSharp.Auth/Models/AuthRequest.cs index d0948d0..f8e573e 100644 --- a/WalletConnectSharp.Auth/Models/AuthRequest.cs +++ b/WalletConnectSharp.Auth/Models/AuthRequest.cs @@ -6,7 +6,7 @@ namespace WalletConnectSharp.Auth.Models; public class AuthRequest : TopicMessage { [JsonProperty("params")] - public AuthRequestData Parameters { get; set; } + public AuthRequestData Parameters; [JsonProperty("verifyContext")] public VerifiedContext VerifyContext { get; set; } diff --git a/WalletConnectSharp.Auth/Models/AuthRequestData.cs b/WalletConnectSharp.Auth/Models/AuthRequestData.cs index 505784d..9f6d8d4 100644 --- a/WalletConnectSharp.Auth/Models/AuthRequestData.cs +++ b/WalletConnectSharp.Auth/Models/AuthRequestData.cs @@ -6,7 +6,7 @@ namespace WalletConnectSharp.Auth.Models; public class AuthRequestData { [JsonProperty("cacaoPayload")] - public Cacao.CacaoRequestPayload CacaoPayload { get; set; } + public Cacao.CacaoRequestPayload CacaoPayload; [JsonProperty("requester")] public Requester Requester { get; set; } diff --git a/WalletConnectSharp.Auth/Models/AuthResponse.cs b/WalletConnectSharp.Auth/Models/AuthResponse.cs index a943d5a..e41d4f9 100644 --- a/WalletConnectSharp.Auth/Models/AuthResponse.cs +++ b/WalletConnectSharp.Auth/Models/AuthResponse.cs @@ -6,5 +6,5 @@ namespace WalletConnectSharp.Auth.Models; public class AuthResponse : TopicMessage { [JsonProperty("params")] - public JsonRpcResponse Response { get; set; } + public JsonRpcResponse Response; } diff --git a/WalletConnectSharp.Auth/Models/ErrorResponse.cs b/WalletConnectSharp.Auth/Models/ErrorResponse.cs index af80776..befb68a 100644 --- a/WalletConnectSharp.Auth/Models/ErrorResponse.cs +++ b/WalletConnectSharp.Auth/Models/ErrorResponse.cs @@ -6,5 +6,5 @@ namespace WalletConnectSharp.Auth.Models; public class ErrorResponse : Message { [JsonProperty("error")] - public Error Error { get; set; } + public Error Error; } diff --git a/WalletConnectSharp.Auth/Models/Message.cs b/WalletConnectSharp.Auth/Models/Message.cs index 4b5ca80..fe2c759 100644 --- a/WalletConnectSharp.Auth/Models/Message.cs +++ b/WalletConnectSharp.Auth/Models/Message.cs @@ -6,7 +6,7 @@ namespace WalletConnectSharp.Auth.Models; public class Message : IKeyHolder { [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)] - public long? Id { get; set; } + public long? Id; public long Key { diff --git a/WalletConnectSharp.Auth/Models/PairingData.cs b/WalletConnectSharp.Auth/Models/PairingData.cs index baf8c1e..ba8da35 100644 --- a/WalletConnectSharp.Auth/Models/PairingData.cs +++ b/WalletConnectSharp.Auth/Models/PairingData.cs @@ -6,7 +6,7 @@ namespace WalletConnectSharp.Auth.Models; public class PairingData : IKeyHolder { [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; [JsonProperty("pairingTopic")] public string PairingTopic { get; set; } diff --git a/WalletConnectSharp.Auth/Models/PayloadParams.cs b/WalletConnectSharp.Auth/Models/PayloadParams.cs index 6120446..7d1c3fd 100644 --- a/WalletConnectSharp.Auth/Models/PayloadParams.cs +++ b/WalletConnectSharp.Auth/Models/PayloadParams.cs @@ -5,7 +5,7 @@ namespace WalletConnectSharp.Auth.Models; public class PayloadParams : AuthPayload { [JsonProperty("version")] - public string Version { get; set; } + public string Version; [JsonProperty("iat")] public string Iat { get; set; } diff --git a/WalletConnectSharp.Auth/Models/PendingRequest.cs b/WalletConnectSharp.Auth/Models/PendingRequest.cs index 3499eaa..bed653b 100644 --- a/WalletConnectSharp.Auth/Models/PendingRequest.cs +++ b/WalletConnectSharp.Auth/Models/PendingRequest.cs @@ -5,7 +5,7 @@ namespace WalletConnectSharp.Auth.Models; public class PendingRequest : Message { [JsonProperty("pairingTopic")] - public string PairingTopic { get; set; } + public string PairingTopic; [JsonProperty("requester")] public Requester Requester { get; set; } diff --git a/WalletConnectSharp.Auth/Models/RequestParams.cs b/WalletConnectSharp.Auth/Models/RequestParams.cs index 69ada36..41ec0a3 100644 --- a/WalletConnectSharp.Auth/Models/RequestParams.cs +++ b/WalletConnectSharp.Auth/Models/RequestParams.cs @@ -5,7 +5,7 @@ namespace WalletConnectSharp.Auth.Models; public class RequestParams : AuthPayload { [JsonProperty("expiry", NullValueHandling = NullValueHandling.Ignore)] - public long? Expiry { get; set; } + public long? Expiry; public RequestParams() { } diff --git a/WalletConnectSharp.Auth/Models/RequestUri.cs b/WalletConnectSharp.Auth/Models/RequestUri.cs index f3f42d3..4f5272a 100644 --- a/WalletConnectSharp.Auth/Models/RequestUri.cs +++ b/WalletConnectSharp.Auth/Models/RequestUri.cs @@ -5,7 +5,7 @@ namespace WalletConnectSharp.Auth.Models; public class RequestUri { [JsonProperty("id")] - public long Id { get; set; } + public long Id; [JsonProperty("uri")] public string Uri { get; set; } diff --git a/WalletConnectSharp.Auth/Models/Requester.cs b/WalletConnectSharp.Auth/Models/Requester.cs index b4a0553..6513e10 100644 --- a/WalletConnectSharp.Auth/Models/Requester.cs +++ b/WalletConnectSharp.Auth/Models/Requester.cs @@ -6,8 +6,8 @@ namespace WalletConnectSharp.Auth.Models; public class Requester { [JsonProperty("metadata")] - public Metadata Metadata { get; set; } + public Metadata Metadata; [JsonProperty("publicKey")] - public string PublicKey { get; set; } + public string PublicKey; } diff --git a/WalletConnectSharp.Auth/Models/ResultResponse.cs b/WalletConnectSharp.Auth/Models/ResultResponse.cs index 2e60457..ed45360 100644 --- a/WalletConnectSharp.Auth/Models/ResultResponse.cs +++ b/WalletConnectSharp.Auth/Models/ResultResponse.cs @@ -5,5 +5,5 @@ namespace WalletConnectSharp.Auth.Models; public class ResultResponse : Message { [JsonProperty("signature")] - public Cacao.CacaoSignature Signature { get; set; } + public Cacao.CacaoSignature Signature; } diff --git a/WalletConnectSharp.Auth/Models/TopicMessage.cs b/WalletConnectSharp.Auth/Models/TopicMessage.cs index 6026473..4c4b826 100644 --- a/WalletConnectSharp.Auth/Models/TopicMessage.cs +++ b/WalletConnectSharp.Auth/Models/TopicMessage.cs @@ -5,5 +5,5 @@ namespace WalletConnectSharp.Auth.Models; public class TopicMessage : Message { [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; } diff --git a/WalletConnectSharp.Auth/WcAuthRequest.cs b/WalletConnectSharp.Auth/WcAuthRequest.cs index dae4db9..13efce6 100644 --- a/WalletConnectSharp.Auth/WcAuthRequest.cs +++ b/WalletConnectSharp.Auth/WcAuthRequest.cs @@ -11,7 +11,7 @@ namespace WalletConnectSharp.Auth; public class WcAuthRequest { [JsonProperty("payloadParams")] - public PayloadParams Payload { get; set; } + public PayloadParams Payload; [JsonProperty("requester")] public Requester Requester { get; set; } diff --git a/WalletConnectSharp.Core/Controllers/Expirer.cs b/WalletConnectSharp.Core/Controllers/Expirer.cs index 15ad9b1..5955cf0 100644 --- a/WalletConnectSharp.Core/Controllers/Expirer.cs +++ b/WalletConnectSharp.Core/Controllers/Expirer.cs @@ -332,7 +332,8 @@ private void Expire(string target, Expiration expiration) private void CheckExpirations() { - foreach (var target in _expirations.Keys) + var clonedArray = _expirations.Keys.ToArray(); + foreach (var target in clonedArray) { var expiration = _expirations[target]; CheckExpiry(target, expiration); diff --git a/WalletConnectSharp.Core/Controllers/Relayer.cs b/WalletConnectSharp.Core/Controllers/Relayer.cs index 6ce7e82..65276dd 100644 --- a/WalletConnectSharp.Core/Controllers/Relayer.cs +++ b/WalletConnectSharp.Core/Controllers/Relayer.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json; using WalletConnectSharp.Common; +using WalletConnectSharp.Common.Logging; using WalletConnectSharp.Common.Model.Errors; using WalletConnectSharp.Common.Utils; using WalletConnectSharp.Core.Interfaces; @@ -140,12 +141,18 @@ public Relayer(RelayerOptions opts) /// public async Task Init() { + WCLogger.Log("[Relayer] Creating provider"); await CreateProvider(); + WCLogger.Log("[Relayer] Opening transport"); + await TransportOpen(); + + WCLogger.Log("[Relayer] Init MessageHandler and Subscriber"); await Task.WhenAll( - Messages.Init(), TransportOpen(), Subscriber.Init() + Messages.Init(), Subscriber.Init() ); + WCLogger.Log("[Relayer] Registering event listeners"); RegisterEventListeners(); initialized = true; @@ -168,27 +175,27 @@ await Task.WhenAll( protected virtual async Task CreateProvider() { var auth = await this.Core.Crypto.SignJwt(this.relayUrl); - Provider = CreateProvider(auth); + Provider = await CreateProvider(auth); RegisterProviderEventListeners(); } - protected virtual IJsonRpcProvider CreateProvider(string auth) + protected virtual async Task CreateProvider(string auth) { - return new JsonRpcProvider( - BuildConnection( - RelayUrl.FormatRelayRpcUrl( - relayUrl, - IRelayer.Protocol, - IRelayer.Version.ToString(), - SDKConstants.SDK_VERSION, - projectId, - auth - ) + var connection = await BuildConnection( + RelayUrl.FormatRelayRpcUrl( + relayUrl, + IRelayer.Protocol, + IRelayer.Version.ToString(), + SDKConstants.SDK_VERSION, + projectId, + auth ) ); + + return new JsonRpcProvider(connection); } - protected virtual IJsonRpcConnection BuildConnection(string url) + protected virtual Task BuildConnection(string url) { return Core.Options.ConnectionBuilder.CreateConnection(url); //return new WebsocketConnection(url); @@ -343,7 +350,10 @@ public Task Unsubscribe(string topic, UnsubscribeOptions opts = null) public async Task Request(IRequestArguments request, object context = null) { + WCLogger.Log("[Relayer] Checking for established connection"); await this.ToEstablishConnection(); + + WCLogger.Log("[Relayer] Sending request through provider"); return await this.Provider.Request(request, context); } @@ -453,6 +463,7 @@ private async Task ToEstablishConnection() // Check for connection while (Connecting) { + WCLogger.Log("[Relayer] Waiting for connection to open"); await Task.Delay(20); } @@ -462,6 +473,7 @@ private async Task ToEstablishConnection() return; } + WCLogger.Log("[Relayer] Restarting transport"); await this.RestartTransport(); } } diff --git a/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs b/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs index 46675bb..8f1e350 100644 --- a/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs +++ b/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using WalletConnectSharp.Common.Logging; using WalletConnectSharp.Common.Model.Errors; using WalletConnectSharp.Core.Interfaces; using WalletConnectSharp.Core.Models.Relay; @@ -305,6 +306,8 @@ public async Task SendRequest(string topic, T parameters, long? exp var method = RpcMethodAttribute.MethodForType(); var payload = new JsonRpcRequest(method, parameters); + + WCLogger.Log(JsonConvert.SerializeObject(payload)); var message = await this.Core.Crypto.Encode(topic, payload, options); diff --git a/WalletConnectSharp.Core/Models/CoreOptions.cs b/WalletConnectSharp.Core/Models/CoreOptions.cs index 138abe5..0d7ebb6 100644 --- a/WalletConnectSharp.Core/Models/CoreOptions.cs +++ b/WalletConnectSharp.Core/Models/CoreOptions.cs @@ -16,26 +16,26 @@ public class CoreOptions /// The Project ID to use to authenticate with the relay server /// [JsonProperty("projectId")] - public string ProjectId { get; set; } + public string ProjectId; /// /// The name that this Core module will show itself as /// [JsonProperty("name")] - public string Name { get; set; } + public string Name; /// /// The URL of the relay server to connect to. This should not include any auth info /// [JsonProperty("relayUrl")] - public string RelayUrl { get; set; } + public string RelayUrl; /// /// The base context string to use for module isolation. If null or empty, then the string /// "{Name}-client" will be used /// [JsonProperty("context")] - public string BaseContext { get; set; } + public string BaseContext; /// /// The module to use for storage. This module will be used by most Core modules @@ -43,21 +43,28 @@ public class CoreOptions /// If this is set to null, then the default /// [JsonProperty("storage")] - public IKeyValueStorage Storage { get; set; } + public IKeyValueStorage Storage; /// /// The module to use for the module. /// If set to null, then the default module will be used with the provided Storage module. /// [JsonProperty("keychain")] - public IKeyChain KeyChain { get; set; } + public IKeyChain KeyChain; /// /// The interface to use inside the Relayer to build /// new websocket connections. /// [JsonProperty("connectionBuilder")] - public IConnectionBuilder ConnectionBuilder { get; set; } + public IConnectionBuilder ConnectionBuilder; + + /// + /// The module to use for crypto operations. This option + /// overrides the KeyChain option. If set to null, then a default Crypto module will be used + /// with either the KeyChain option or a default keychain + /// + public ICrypto CryptoModule; } } diff --git a/WalletConnectSharp.Core/Models/Eth/EthCall.cs b/WalletConnectSharp.Core/Models/Eth/EthCall.cs index 46a3ff3..f2d879a 100644 --- a/WalletConnectSharp.Core/Models/Eth/EthCall.cs +++ b/WalletConnectSharp.Core/Models/Eth/EthCall.cs @@ -4,9 +4,7 @@ namespace WalletConnectSharp.Core.Models.Eth; public class EthCall { - [JsonProperty("to")] - public string To { get; set; } - - [JsonProperty("data")] - public string Data { get; set; } + [JsonProperty("to")] public string To; + + [JsonProperty("data")] public string Data; } diff --git a/WalletConnectSharp.Core/Models/Expirer/Expiration.cs b/WalletConnectSharp.Core/Models/Expirer/Expiration.cs index 8297c3c..00752ba 100644 --- a/WalletConnectSharp.Core/Models/Expirer/Expiration.cs +++ b/WalletConnectSharp.Core/Models/Expirer/Expiration.cs @@ -13,13 +13,11 @@ public class Expiration /// * id:123 /// * topic:my_topic_string /// - [JsonProperty("target")] - public string Target { get; set; } - + [JsonProperty("target")] public string Target; + /// /// The expiration date, as a unix timestamp (seconds) /// - [JsonProperty("expiry")] - public long Expiry { get; set; } + [JsonProperty("expiry")] public long Expiry; } } diff --git a/WalletConnectSharp.Core/Models/Expirer/ExpirerEventArgs.cs b/WalletConnectSharp.Core/Models/Expirer/ExpirerEventArgs.cs index 1ef6a50..d767922 100644 --- a/WalletConnectSharp.Core/Models/Expirer/ExpirerEventArgs.cs +++ b/WalletConnectSharp.Core/Models/Expirer/ExpirerEventArgs.cs @@ -11,12 +11,12 @@ public class ExpirerEventArgs /// The target this expiration is for /// [JsonProperty("target")] - public string Target { get; set; } + public string Target; /// /// The expiration data for this event /// [JsonProperty("expiration")] - public Expiration Expiration { get; set; } + public Expiration Expiration; } } diff --git a/WalletConnectSharp.Core/Models/Expirer/ExpirerTarget.cs b/WalletConnectSharp.Core/Models/Expirer/ExpirerTarget.cs index 0fed073..feeb36e 100644 --- a/WalletConnectSharp.Core/Models/Expirer/ExpirerTarget.cs +++ b/WalletConnectSharp.Core/Models/Expirer/ExpirerTarget.cs @@ -16,14 +16,14 @@ public class ExpirerTarget /// this field will be null /// [JsonProperty("id")] - public long? Id { get; set; } + public long? Id; /// /// The resulting Topic from the given . If the did not include a Topic, then /// this field will be null /// [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; /// /// Create a new instance of this class with a given . The given will diff --git a/WalletConnectSharp.Core/Models/History/JsonRpcRecord.cs b/WalletConnectSharp.Core/Models/History/JsonRpcRecord.cs index 17d8e98..ced84e4 100644 --- a/WalletConnectSharp.Core/Models/History/JsonRpcRecord.cs +++ b/WalletConnectSharp.Core/Models/History/JsonRpcRecord.cs @@ -16,19 +16,19 @@ public class JsonRpcRecord /// The id of the JSON RPC request /// [JsonProperty("id")] - public long Id { get; set; } + public long Id; /// /// The topic the request was sent in /// [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; /// /// The request data for this JSON RPC record /// [JsonProperty("request")] - public IRequestArguments Request { get; set; } + public IRequestArguments Request; /// /// The chainId this request is intended for diff --git a/WalletConnectSharp.Core/Models/History/RequestEvent.cs b/WalletConnectSharp.Core/Models/History/RequestEvent.cs index 39d3c19..89b2bc4 100644 --- a/WalletConnectSharp.Core/Models/History/RequestEvent.cs +++ b/WalletConnectSharp.Core/Models/History/RequestEvent.cs @@ -15,19 +15,19 @@ public class RequestEvent /// The topic the request was sent in /// [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; /// /// The request parameters sent /// [JsonProperty("request")] - public IRequestArguments Request { get; set; } + public IRequestArguments Request; /// /// The chainId this request is intended for /// [JsonProperty("chainId")] - public string ChainId { get; set; } + public string ChainId; /// /// A helper function to create a new RequestEvent from a diff --git a/WalletConnectSharp.Core/Models/MessageHandler/RequestEventArgs.cs b/WalletConnectSharp.Core/Models/MessageHandler/RequestEventArgs.cs index 853de09..4e7d6b4 100644 --- a/WalletConnectSharp.Core/Models/MessageHandler/RequestEventArgs.cs +++ b/WalletConnectSharp.Core/Models/MessageHandler/RequestEventArgs.cs @@ -31,16 +31,16 @@ public class RequestEventArgs /// If the field is non-null, then this field will not be sent and the /// will be sent instead /// - public TR Response { get; set; } + public TR Response; /// /// The current error to send when this event finishes propagating. You can set this value /// to send an Error response when this event completes. /// This value will always override if the value is non-null /// - public Error Error { get; set; } + public Error Error; - public VerifiedContext VerifiedContext { get; set; } + public VerifiedContext VerifiedContext; internal RequestEventArgs(string topic, JsonRpcRequest request, VerifiedContext context) { diff --git a/WalletConnectSharp.Core/Models/Pairing/CreatePairingData.cs b/WalletConnectSharp.Core/Models/Pairing/CreatePairingData.cs index d84f501..e2726a3 100644 --- a/WalletConnectSharp.Core/Models/Pairing/CreatePairingData.cs +++ b/WalletConnectSharp.Core/Models/Pairing/CreatePairingData.cs @@ -12,12 +12,12 @@ public class CreatePairingData /// The new pairing topic /// [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; /// /// The URI the wallet should use to pair & retrieve the session proposal /// [JsonProperty("uri")] - public string Uri { get; set; } + public string Uri; } } diff --git a/WalletConnectSharp.Core/Models/Pairing/PairingEvent.cs b/WalletConnectSharp.Core/Models/Pairing/PairingEvent.cs index 54fe386..995aefe 100644 --- a/WalletConnectSharp.Core/Models/Pairing/PairingEvent.cs +++ b/WalletConnectSharp.Core/Models/Pairing/PairingEvent.cs @@ -14,12 +14,12 @@ public class PairingEvent /// The ID of the JSON Rpc request that triggered this session event /// [JsonProperty("id")] - public long Id { get; set; } + public long Id; /// /// The topic of the session this event took place in /// [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; } } diff --git a/WalletConnectSharp.Core/Models/Pairing/PairingStruct.cs b/WalletConnectSharp.Core/Models/Pairing/PairingStruct.cs index aca498b..7301695 100644 --- a/WalletConnectSharp.Core/Models/Pairing/PairingStruct.cs +++ b/WalletConnectSharp.Core/Models/Pairing/PairingStruct.cs @@ -14,7 +14,7 @@ public struct PairingStruct : IKeyHolder /// The topic the pairing took place in /// [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; /// /// This is the key field, mapped to the Topic. Implemented for @@ -33,24 +33,24 @@ public string Key /// When this pairing expires /// [JsonProperty("expiry")] - public long? Expiry { get; set; } + public long? Expiry; /// /// Relay protocol options for this pairing /// [JsonProperty("relay")] - public ProtocolOptions Relay { get; set; } + public ProtocolOptions Relay; /// /// Whether this pairing is active or not /// [JsonProperty("active")] - public bool? Active { get; set; } + public bool? Active; /// /// The metadata of the peer this pairing is with /// [JsonProperty("peerMetadata")] - public Metadata PeerMetadata { get; set; } + public Metadata PeerMetadata; } } diff --git a/WalletConnectSharp.Core/Models/Pairing/UriParameters.cs b/WalletConnectSharp.Core/Models/Pairing/UriParameters.cs index 1553a26..45885e2 100644 --- a/WalletConnectSharp.Core/Models/Pairing/UriParameters.cs +++ b/WalletConnectSharp.Core/Models/Pairing/UriParameters.cs @@ -13,30 +13,30 @@ public class UriParameters /// The protocol being used for this session (as a protocol string) /// [JsonProperty("protocol")] - public string Protocol { get; set; } + public string Protocol; /// /// The protocol version being used for this session /// [JsonProperty("version")] - public int Version { get; set; } + public int Version; /// /// The pairing topic that should be used to retrieve the session proposal /// [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; /// /// The sym key used to encrypt the session proposal /// [JsonProperty("symKey")] - public string SymKey { get; set; } + public string SymKey; /// /// Any protocol options that should be used when pairing / approving the session /// [JsonProperty("relay")] - public ProtocolOptions Relay { get; set; } + public ProtocolOptions Relay; } } diff --git a/WalletConnectSharp.Core/Models/Publisher/PublishParams.cs b/WalletConnectSharp.Core/Models/Publisher/PublishParams.cs index e51ee03..b120db9 100644 --- a/WalletConnectSharp.Core/Models/Publisher/PublishParams.cs +++ b/WalletConnectSharp.Core/Models/Publisher/PublishParams.cs @@ -12,18 +12,18 @@ public class PublishParams /// The topic to publish the message to /// [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; /// /// The message to publish in the set topic /// [JsonProperty("message")] - public string Message { get; set; } + public string Message; /// /// The required PublishOptions to use when publishing /// [JsonProperty("opts")] - public PublishOptions Options { get; set; } + public PublishOptions Options; } } diff --git a/WalletConnectSharp.Core/Models/Relay/DecodedMessageEvent.cs b/WalletConnectSharp.Core/Models/Relay/DecodedMessageEvent.cs index 7389d7d..0fb26f2 100644 --- a/WalletConnectSharp.Core/Models/Relay/DecodedMessageEvent.cs +++ b/WalletConnectSharp.Core/Models/Relay/DecodedMessageEvent.cs @@ -12,6 +12,6 @@ public class DecodedMessageEvent : MessageEvent /// The deserialized payload that was decoded from the Message property /// [JsonProperty("payload")] - public JsonRpcPayload Payload { get; set; } + public JsonRpcPayload Payload; } } diff --git a/WalletConnectSharp.Core/Models/Relay/MessageEvent.cs b/WalletConnectSharp.Core/Models/Relay/MessageEvent.cs index 05e3de0..4b44564 100644 --- a/WalletConnectSharp.Core/Models/Relay/MessageEvent.cs +++ b/WalletConnectSharp.Core/Models/Relay/MessageEvent.cs @@ -12,12 +12,12 @@ public class MessageEvent /// The topic the message was sent / received in /// [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; /// /// The message that was sent / received /// [JsonProperty("message")] - public string Message { get; set; } + public string Message; } } diff --git a/WalletConnectSharp.Core/Models/Relay/ProtocolOptionHolder.cs b/WalletConnectSharp.Core/Models/Relay/ProtocolOptionHolder.cs index ddd2be1..0a9f276 100644 --- a/WalletConnectSharp.Core/Models/Relay/ProtocolOptionHolder.cs +++ b/WalletConnectSharp.Core/Models/Relay/ProtocolOptionHolder.cs @@ -11,6 +11,6 @@ public abstract class ProtocolOptionHolder /// The relay protocol options to use for this event /// [JsonProperty("relay")] - public ProtocolOptions Relay { get; set; } + public ProtocolOptions Relay; } } diff --git a/WalletConnectSharp.Core/Models/Relay/ProtocolOptions.cs b/WalletConnectSharp.Core/Models/Relay/ProtocolOptions.cs index ac0d0f5..a1886ee 100644 --- a/WalletConnectSharp.Core/Models/Relay/ProtocolOptions.cs +++ b/WalletConnectSharp.Core/Models/Relay/ProtocolOptions.cs @@ -11,12 +11,12 @@ public class ProtocolOptions /// The protocol to use when communicating with the relay server /// [JsonProperty("protocol")] - public string Protocol { get; set; } + public string Protocol; /// /// Additional protocol data /// - [JsonProperty("data")] - public string Data { get; set; } + [JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)] + public string Data; } } diff --git a/WalletConnectSharp.Core/Models/Relay/PublishOptions.cs b/WalletConnectSharp.Core/Models/Relay/PublishOptions.cs index 74d692e..f2fa328 100644 --- a/WalletConnectSharp.Core/Models/Relay/PublishOptions.cs +++ b/WalletConnectSharp.Core/Models/Relay/PublishOptions.cs @@ -11,12 +11,12 @@ public class PublishOptions : ProtocolOptionHolder /// Time To Live value for the message being published. /// [JsonProperty("ttl")] - public long TTL { get; set; } + public long TTL; /// /// A Tag for the message /// [JsonProperty("tag")] - public long Tag { get; set; } + public long Tag; } } diff --git a/WalletConnectSharp.Core/Models/Relay/RelayPublishRequest.cs b/WalletConnectSharp.Core/Models/Relay/RelayPublishRequest.cs index 22f1c56..9f40473 100644 --- a/WalletConnectSharp.Core/Models/Relay/RelayPublishRequest.cs +++ b/WalletConnectSharp.Core/Models/Relay/RelayPublishRequest.cs @@ -12,13 +12,13 @@ public class RelayPublishRequest /// The topic to publish the message under /// [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; /// /// The message to publish to the relay server /// [JsonProperty("message")] - public string Message { get; set; } + public string Message; /// /// Time To Live. How long the message will remain on the relay server without being diff --git a/WalletConnectSharp.Core/Models/Relay/RelayerOptions.cs b/WalletConnectSharp.Core/Models/Relay/RelayerOptions.cs index fc50e75..0a1d645 100644 --- a/WalletConnectSharp.Core/Models/Relay/RelayerOptions.cs +++ b/WalletConnectSharp.Core/Models/Relay/RelayerOptions.cs @@ -14,14 +14,14 @@ public class RelayerOptions /// module requires the core modules to function properly /// [JsonProperty("core")] - public ICore Core { get; set; } + public ICore Core; /// /// The URL of the Relay server to connect to. This should not include any auth information, the Relayer module /// will construct it's own auth token using the project ID specified /// [JsonProperty("relayUrl")] - public string RelayUrl { get; set; } + public string RelayUrl; /// /// The project ID to use for Relay authentication diff --git a/WalletConnectSharp.Core/Models/Relay/UnsubscribeOptions.cs b/WalletConnectSharp.Core/Models/Relay/UnsubscribeOptions.cs index b69a282..4e2c2b1 100644 --- a/WalletConnectSharp.Core/Models/Relay/UnsubscribeOptions.cs +++ b/WalletConnectSharp.Core/Models/Relay/UnsubscribeOptions.cs @@ -11,6 +11,6 @@ public class UnsubscribeOptions : ProtocolOptionHolder /// The id of the subscription to unsubscribe from /// [JsonProperty("id")] - public string Id { get; set; } + public string Id; } } diff --git a/WalletConnectSharp.Core/Models/Subscriber/ActiveSubscription.cs b/WalletConnectSharp.Core/Models/Subscriber/ActiveSubscription.cs index f48ddb6..cad81ca 100644 --- a/WalletConnectSharp.Core/Models/Subscriber/ActiveSubscription.cs +++ b/WalletConnectSharp.Core/Models/Subscriber/ActiveSubscription.cs @@ -11,6 +11,6 @@ public class ActiveSubscription : PendingSubscription /// The id of the subscription /// [JsonProperty("id")] - public string Id { get; set; } + public string Id; } } diff --git a/WalletConnectSharp.Core/Models/Subscriber/BatchSubscribeParams.cs b/WalletConnectSharp.Core/Models/Subscriber/BatchSubscribeParams.cs index 2d2bd17..756a81a 100644 --- a/WalletConnectSharp.Core/Models/Subscriber/BatchSubscribeParams.cs +++ b/WalletConnectSharp.Core/Models/Subscriber/BatchSubscribeParams.cs @@ -5,6 +5,6 @@ namespace WalletConnectSharp.Core.Models.Subscriber public class BatchSubscribeParams { [JsonProperty("topics")] - public string[] Topics { get; set; } + public string[] Topics; } } diff --git a/WalletConnectSharp.Core/Models/Subscriber/DeletedSubscription.cs b/WalletConnectSharp.Core/Models/Subscriber/DeletedSubscription.cs index ff38c80..9df3e11 100644 --- a/WalletConnectSharp.Core/Models/Subscriber/DeletedSubscription.cs +++ b/WalletConnectSharp.Core/Models/Subscriber/DeletedSubscription.cs @@ -12,6 +12,6 @@ public class DeletedSubscription : ActiveSubscription /// The reason why the subscription was deleted /// [JsonProperty("reason")] - public Error Reason { get; set; } + public Error Reason; } } diff --git a/WalletConnectSharp.Core/Models/Subscriber/JsonRpcSubscriberParams.cs b/WalletConnectSharp.Core/Models/Subscriber/JsonRpcSubscriberParams.cs index 2b076f3..80fa01c 100644 --- a/WalletConnectSharp.Core/Models/Subscriber/JsonRpcSubscriberParams.cs +++ b/WalletConnectSharp.Core/Models/Subscriber/JsonRpcSubscriberParams.cs @@ -11,6 +11,6 @@ public class JsonRpcSubscriberParams /// The topic to subscribe to /// [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; } } diff --git a/WalletConnectSharp.Core/Models/Subscriber/JsonRpcSubscriptionParams.cs b/WalletConnectSharp.Core/Models/Subscriber/JsonRpcSubscriptionParams.cs index d63e9ad..fc005de 100644 --- a/WalletConnectSharp.Core/Models/Subscriber/JsonRpcSubscriptionParams.cs +++ b/WalletConnectSharp.Core/Models/Subscriber/JsonRpcSubscriptionParams.cs @@ -12,12 +12,12 @@ public class JsonRpcSubscriptionParams /// The id of the subscription the message came from /// [JsonProperty("id")] - public string Id { get; set; } + public string Id; /// /// The message data /// [JsonProperty("data")] - public MessageData Data { get; set; } + public MessageData Data; } } diff --git a/WalletConnectSharp.Core/Models/Subscriber/JsonRpcUnsubscribeParams.cs b/WalletConnectSharp.Core/Models/Subscriber/JsonRpcUnsubscribeParams.cs index 43047f5..381aa20 100644 --- a/WalletConnectSharp.Core/Models/Subscriber/JsonRpcUnsubscribeParams.cs +++ b/WalletConnectSharp.Core/Models/Subscriber/JsonRpcUnsubscribeParams.cs @@ -11,12 +11,12 @@ public class JsonRpcUnsubscribeParams /// The subscription id to unsubscribe from /// [JsonProperty("id")] - public string Id { get; set; } + public string Id; /// /// The topic the subscription exists in /// [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; } } diff --git a/WalletConnectSharp.Core/Models/Subscriber/MessageData.cs b/WalletConnectSharp.Core/Models/Subscriber/MessageData.cs index 89f931d..84d93af 100644 --- a/WalletConnectSharp.Core/Models/Subscriber/MessageData.cs +++ b/WalletConnectSharp.Core/Models/Subscriber/MessageData.cs @@ -12,7 +12,7 @@ public class MessageData /// The topic the message came from /// [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; /// /// The message as a string diff --git a/WalletConnectSharp.Core/Models/Subscriber/PendingSubscription.cs b/WalletConnectSharp.Core/Models/Subscriber/PendingSubscription.cs index a47d363..840bafc 100644 --- a/WalletConnectSharp.Core/Models/Subscriber/PendingSubscription.cs +++ b/WalletConnectSharp.Core/Models/Subscriber/PendingSubscription.cs @@ -12,6 +12,6 @@ public class PendingSubscription : SubscribeOptions /// The topic that will be subscribed to /// [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; } } diff --git a/WalletConnectSharp.Core/Models/Verify/VerifiedContext.cs b/WalletConnectSharp.Core/Models/Verify/VerifiedContext.cs index 7b4530a..eb8d593 100644 --- a/WalletConnectSharp.Core/Models/Verify/VerifiedContext.cs +++ b/WalletConnectSharp.Core/Models/Verify/VerifiedContext.cs @@ -5,7 +5,7 @@ namespace WalletConnectSharp.Core.Models.Verify; public class VerifiedContext { [JsonProperty("origin")] - public string Origin { get; set; } + public string Origin; [JsonProperty("validation")] private string _validation; diff --git a/WalletConnectSharp.Core/WalletConnectCore.cs b/WalletConnectSharp.Core/WalletConnectCore.cs index c5b5228..57cf270 100644 --- a/WalletConnectSharp.Core/WalletConnectCore.cs +++ b/WalletConnectSharp.Core/WalletConnectCore.cs @@ -138,18 +138,28 @@ public WalletConnectCore(CoreOptions options = null) options.Storage = new FileSystemStorage(); } - if (options.KeyChain == null) - { - options.KeyChain = new KeyChain(options.Storage); - } - + options.ConnectionBuilder ??= new WebsocketConnectionBuilder(); Options = options; ProjectId = options.ProjectId; RelayUrl = options.RelayUrl; - Crypto = new Crypto.Crypto(options.KeyChain); Storage = options.Storage; + + if (options.CryptoModule != null) + { + Crypto = options.CryptoModule; + } + else + { + if (options.KeyChain == null) + { + options.KeyChain = new KeyChain(options.Storage); + } + + Crypto = new Crypto.Crypto(options.KeyChain); + } + HeartBeat = new HeartBeat(); _optName = options.Name; Events = new EventDelegator(this); diff --git a/WalletConnectSharp.Sign/Engine.cs b/WalletConnectSharp.Sign/Engine.cs index 91dfb66..e3d0d33 100644 --- a/WalletConnectSharp.Sign/Engine.cs +++ b/WalletConnectSharp.Sign/Engine.cs @@ -1,5 +1,7 @@ using System.Text.RegularExpressions; +using Newtonsoft.Json; using WalletConnectSharp.Common; +using WalletConnectSharp.Common.Logging; using WalletConnectSharp.Common.Model.Errors; using WalletConnectSharp.Common.Model.Relay; using WalletConnectSharp.Common.Utils; @@ -214,6 +216,8 @@ public async Task Connect(ConnectOptions options) var pairing = this.Client.Core.Pairing.Store.Get(topic); if (pairing.Active != null) active = pairing.Active.Value; + + WCLogger.Log($"Loaded pairing for {topic}"); } if (string.IsNullOrEmpty(topic) || !active) @@ -221,6 +225,8 @@ public async Task Connect(ConnectOptions options) var CreatePairing = await this.Client.Core.Pairing.Create(); topic = CreatePairing.Topic; uri = CreatePairing.Uri; + + WCLogger.Log($"Created pairing for new topic: {topic}"); } var publicKey = await this.Client.Core.Crypto.GenerateKeyPair(); @@ -244,6 +250,8 @@ public async Task Connect(ConnectOptions options) OptionalNamespaces = optionalNamespaces, SessionProperties = sessionProperties, }; + + WCLogger.Log($"Created public key pair"); TaskCompletionSource approvalTask = new TaskCompletionSource(); this.Events.ListenForOnce("session_connect", async (sender, e) => @@ -279,6 +287,8 @@ public async Task Connect(ConnectOptions options) throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY, $"connect() pairing topic: {topic}"); } + WCLogger.Log($"Sending request JSON {JsonConvert.SerializeObject(proposal)} to topic {topic}"); + var id = await MessageHandler.SendRequest(topic, proposal); var expiry = Clock.CalculateExpiry(Clock.FIVE_MINUTES); diff --git a/WalletConnectSharp.Sign/Models/Engine/ApproveParams.cs b/WalletConnectSharp.Sign/Models/Engine/ApproveParams.cs index 637a537..13e79aa 100644 --- a/WalletConnectSharp.Sign/Models/Engine/ApproveParams.cs +++ b/WalletConnectSharp.Sign/Models/Engine/ApproveParams.cs @@ -12,24 +12,24 @@ public class ApproveParams /// The id of the session proposal to approve /// [JsonProperty("id")] - public long Id { get; set; } + public long Id; /// /// The enabled namespaces in this session /// [JsonProperty("namespaces")] - public Namespaces Namespaces { get; set; } + public Namespaces Namespaces; /// /// The relay protocol that will be used in this session (as a protocol string) /// [JsonProperty("relayProtocol")] - public string RelayProtocol { get; set; } + public string RelayProtocol; /// /// Custom session properties for this approval /// [JsonProperty("sessionProperties")] - public Dictionary SessionProperties { get; set; } + public Dictionary SessionProperties; } } diff --git a/WalletConnectSharp.Sign/Models/Engine/ConnectOptions.cs b/WalletConnectSharp.Sign/Models/Engine/ConnectOptions.cs index 7b28583..cd8ad59 100644 --- a/WalletConnectSharp.Sign/Models/Engine/ConnectOptions.cs +++ b/WalletConnectSharp.Sign/Models/Engine/ConnectOptions.cs @@ -13,19 +13,19 @@ public class ConnectOptions /// The required namespaces that will be required for this session /// [JsonProperty("requiredNamespaces")] - public RequiredNamespaces RequiredNamespaces { get; set; } + public RequiredNamespaces RequiredNamespaces; /// /// The optional namespaces for this session /// [JsonProperty("optionalNamespaces")] - public Dictionary OptionalNamespaces { get; set; } + public Dictionary OptionalNamespaces; /// /// Custom session properties for this session /// [JsonProperty("sessionProperties", NullValueHandling = NullValueHandling.Ignore)] - public Dictionary SessionProperties { get; set; } + public Dictionary SessionProperties; /// /// The pairing topic to be used to store the session proposal. By default, this should be left blank so @@ -33,14 +33,14 @@ public class ConnectOptions /// that pairing topic can be used, however the pairing topic MUST exist in storage. /// [JsonProperty("pairingTopic")] - public string PairingTopic { get; set; } + public string PairingTopic; /// /// The protocol options to use for this session. This is optional and defaults to /// value is set. /// [JsonProperty("relays")] - public ProtocolOptions Relays { get; set; } + public ProtocolOptions Relays; /// /// Create blank options with no required namespaces diff --git a/WalletConnectSharp.Sign/Models/Engine/ConnectedData.cs b/WalletConnectSharp.Sign/Models/Engine/ConnectedData.cs index ad3aadb..ff4d30a 100644 --- a/WalletConnectSharp.Sign/Models/Engine/ConnectedData.cs +++ b/WalletConnectSharp.Sign/Models/Engine/ConnectedData.cs @@ -12,12 +12,12 @@ public class ConnectedData /// The URI that can be used to retrieve the submitted session proposal. This should be shared /// SECURELY out-of-band to a wallet supporting the SDK /// - public string Uri { get; set; } + public string Uri; /// /// A task that will resolve to an approved session. If the session proposal is rejected, then this /// task will throw an exception. /// - public Task Approval { get; set; } + public Task Approval; } } diff --git a/WalletConnectSharp.Sign/Models/Engine/Events/EmitEvent.cs b/WalletConnectSharp.Sign/Models/Engine/Events/EmitEvent.cs index d094068..9107f5b 100644 --- a/WalletConnectSharp.Sign/Models/Engine/Events/EmitEvent.cs +++ b/WalletConnectSharp.Sign/Models/Engine/Events/EmitEvent.cs @@ -13,6 +13,6 @@ public class EmitEvent : SessionEvent /// The wc_sessionEvent request that triggered this event /// [JsonProperty("params")] - public SessionEvent Params { get; set; } + public SessionEvent Params; } } diff --git a/WalletConnectSharp.Sign/Models/Engine/Events/EventData.cs b/WalletConnectSharp.Sign/Models/Engine/Events/EventData.cs index 91117d8..737a433 100644 --- a/WalletConnectSharp.Sign/Models/Engine/Events/EventData.cs +++ b/WalletConnectSharp.Sign/Models/Engine/Events/EventData.cs @@ -12,12 +12,11 @@ public class EventData /// The name of the event /// [JsonProperty("name")] - public string Name { get; set; } - + public string Name; + /// /// The event data associated with this event /// - [JsonProperty("data")] - public T Data { get; set; } + [JsonProperty("data")] public T Data; } } diff --git a/WalletConnectSharp.Sign/Models/Engine/Events/SessionEvent.cs b/WalletConnectSharp.Sign/Models/Engine/Events/SessionEvent.cs index f9de238..4a10c7e 100644 --- a/WalletConnectSharp.Sign/Models/Engine/Events/SessionEvent.cs +++ b/WalletConnectSharp.Sign/Models/Engine/Events/SessionEvent.cs @@ -19,12 +19,12 @@ public class SessionEvent /// The ID of the JSON Rpc request that triggered this session event /// [JsonProperty("id")] - public long Id { get; set; } + public long Id; /// /// The topic of the session this event took place in /// [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; } } diff --git a/WalletConnectSharp.Sign/Models/Engine/Events/SessionProposalEvent.cs b/WalletConnectSharp.Sign/Models/Engine/Events/SessionProposalEvent.cs index 4a10731..fc28ca3 100644 --- a/WalletConnectSharp.Sign/Models/Engine/Events/SessionProposalEvent.cs +++ b/WalletConnectSharp.Sign/Models/Engine/Events/SessionProposalEvent.cs @@ -6,12 +6,12 @@ namespace WalletConnectSharp.Sign.Models.Engine.Events public class SessionProposalEvent { [JsonProperty("id")] - public long Id { get; set; } + public long Id; [JsonProperty("params")] - public ProposalStruct Proposal { get; set; } + public ProposalStruct Proposal; [JsonProperty("verifyContext")] - public VerifiedContext VerifiedContext { get; set; } + public VerifiedContext VerifiedContext; } } diff --git a/WalletConnectSharp.Sign/Models/Engine/Events/SessionRequestEvent.cs b/WalletConnectSharp.Sign/Models/Engine/Events/SessionRequestEvent.cs index 901041f..225ac54 100644 --- a/WalletConnectSharp.Sign/Models/Engine/Events/SessionRequestEvent.cs +++ b/WalletConnectSharp.Sign/Models/Engine/Events/SessionRequestEvent.cs @@ -13,12 +13,12 @@ public class SessionRequestEvent : SessionEvent /// The chainId this request should be performed in /// [JsonProperty("chainId")] - public string ChainId { get; set; } + public string ChainId; /// /// The request arguments of this session request /// [JsonProperty("request")] - public IRequestArguments Request { get; set; } + public IRequestArguments Request; } } diff --git a/WalletConnectSharp.Sign/Models/Engine/Events/SessionUpdateEvent.cs b/WalletConnectSharp.Sign/Models/Engine/Events/SessionUpdateEvent.cs index 887be17..c3d3ca5 100644 --- a/WalletConnectSharp.Sign/Models/Engine/Events/SessionUpdateEvent.cs +++ b/WalletConnectSharp.Sign/Models/Engine/Events/SessionUpdateEvent.cs @@ -12,6 +12,6 @@ public class SessionUpdateEvent : SessionEvent /// The wc_sessionUpdate request that triggered this event /// [JsonProperty("params")] - public SessionUpdate Params { get; set; } + public SessionUpdate Params; } } diff --git a/WalletConnectSharp.Sign/Models/Engine/Methods/SessionEvent.cs b/WalletConnectSharp.Sign/Models/Engine/Methods/SessionEvent.cs index 16c882b..fca2666 100644 --- a/WalletConnectSharp.Sign/Models/Engine/Methods/SessionEvent.cs +++ b/WalletConnectSharp.Sign/Models/Engine/Methods/SessionEvent.cs @@ -19,15 +19,15 @@ public class SessionEvent : IWcMethod /// The chainId this event took place in /// [JsonProperty("chainId")] - public string ChainId { get; set; } + public string ChainId; [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; /// /// The event data /// [JsonProperty("event")] - public EventData Event { get; set; } + public EventData Event; } } diff --git a/WalletConnectSharp.Sign/Models/Engine/Methods/SessionPropose.cs b/WalletConnectSharp.Sign/Models/Engine/Methods/SessionPropose.cs index 8f8bdd4..381e6b4 100644 --- a/WalletConnectSharp.Sign/Models/Engine/Methods/SessionPropose.cs +++ b/WalletConnectSharp.Sign/Models/Engine/Methods/SessionPropose.cs @@ -19,30 +19,30 @@ public class SessionPropose : IWcMethod /// Protocol options that should be used during the session /// [JsonProperty("relays")] - public ProtocolOptions[] Relays { get; set; } + public ProtocolOptions[] Relays; /// /// The required namespaces this session will require /// [JsonProperty("requiredNamespaces")] - public RequiredNamespaces RequiredNamespaces { get; set; } + public RequiredNamespaces RequiredNamespaces; /// /// The optional namespaces for this session /// [JsonProperty("optionalNamespaces")] - public Dictionary OptionalNamespaces { get; set; } + public Dictionary OptionalNamespaces; /// /// Custom session properties for this session /// [JsonProperty("sessionProperties")] - public Dictionary SessionProperties { get; set; } + public Dictionary SessionProperties; /// /// The that created this session proposal /// [JsonProperty("proposer")] - public Participant Proposer { get; set; } + public Participant Proposer; } } diff --git a/WalletConnectSharp.Sign/Models/Engine/Methods/SessionProposeResponse.cs b/WalletConnectSharp.Sign/Models/Engine/Methods/SessionProposeResponse.cs index e0ef7c8..0b891c4 100644 --- a/WalletConnectSharp.Sign/Models/Engine/Methods/SessionProposeResponse.cs +++ b/WalletConnectSharp.Sign/Models/Engine/Methods/SessionProposeResponse.cs @@ -15,12 +15,12 @@ public class SessionProposeResponse /// The protocol options that should be used in this session /// [JsonProperty("relay")] - public ProtocolOptions Relay { get; set; } + public ProtocolOptions Relay; /// /// The public key of the responder to this session proposal /// [JsonProperty("responderPublicKey")] - public string ResponderPublicKey { get; set; } + public string ResponderPublicKey; } } diff --git a/WalletConnectSharp.Sign/Models/Engine/Methods/SessionRequest.cs b/WalletConnectSharp.Sign/Models/Engine/Methods/SessionRequest.cs index 70202a2..e317853 100644 --- a/WalletConnectSharp.Sign/Models/Engine/Methods/SessionRequest.cs +++ b/WalletConnectSharp.Sign/Models/Engine/Methods/SessionRequest.cs @@ -18,12 +18,12 @@ public class SessionRequest : IWcMethod /// The chainId this request should be performed in /// [JsonProperty("chainId")] - public string ChainId { get; set; } + public string ChainId; /// /// The JSON RPC request to send to the peer /// [JsonProperty("request")] - public JsonRpcRequest Request { get; set; } + public JsonRpcRequest Request; } } diff --git a/WalletConnectSharp.Sign/Models/Engine/Methods/SessionSettle.cs b/WalletConnectSharp.Sign/Models/Engine/Methods/SessionSettle.cs index af270b4..6b50e75 100644 --- a/WalletConnectSharp.Sign/Models/Engine/Methods/SessionSettle.cs +++ b/WalletConnectSharp.Sign/Models/Engine/Methods/SessionSettle.cs @@ -18,24 +18,24 @@ public class SessionSettle : IWcMethod /// The protocol options that should be used in this session /// [JsonProperty("relay")] - public ProtocolOptions Relay { get; set; } + public ProtocolOptions Relay; /// /// All namespaces that are enabled in this session /// [JsonProperty("namespaces")] - public Namespaces Namespaces { get; set; } + public Namespaces Namespaces; /// /// When this session will expire /// [JsonProperty("expiry")] - public long Expiry { get; set; } + public long Expiry; /// /// The controlling in this session. In most cases, this is the dApp. /// [JsonProperty("controller")] - public Participant Controller { get; set; } + public Participant Controller; } } diff --git a/WalletConnectSharp.Sign/Models/Engine/Methods/SessionUpdate.cs b/WalletConnectSharp.Sign/Models/Engine/Methods/SessionUpdate.cs index 50f6e29..19d7116 100644 --- a/WalletConnectSharp.Sign/Models/Engine/Methods/SessionUpdate.cs +++ b/WalletConnectSharp.Sign/Models/Engine/Methods/SessionUpdate.cs @@ -18,6 +18,6 @@ public class SessionUpdate : IWcMethod /// The updated namespaces that are enabled for this session /// [JsonProperty("namespaces")] - public Namespaces Namespaces { get; set; } + public Namespaces Namespaces; } } diff --git a/WalletConnectSharp.Sign/Models/Engine/RejectParams.cs b/WalletConnectSharp.Sign/Models/Engine/RejectParams.cs index e899b60..661f46a 100644 --- a/WalletConnectSharp.Sign/Models/Engine/RejectParams.cs +++ b/WalletConnectSharp.Sign/Models/Engine/RejectParams.cs @@ -14,12 +14,12 @@ public class RejectParams /// The id of the session proposal to reject /// [JsonProperty("id")] - public long Id { get; set; } + public long Id; /// /// The reason the session proposal was rejected, as an /// [JsonProperty("reason")] - public Error Reason { get; set; } + public Error Reason; } } diff --git a/WalletConnectSharp.Sign/Models/Namespace.cs b/WalletConnectSharp.Sign/Models/Namespace.cs index 1a2545e..ad364f3 100644 --- a/WalletConnectSharp.Sign/Models/Namespace.cs +++ b/WalletConnectSharp.Sign/Models/Namespace.cs @@ -12,19 +12,19 @@ public class Namespace /// An array of all accounts enabled in this namespace /// [JsonProperty("accounts")] - public string[] Accounts { get; set; } + public string[] Accounts; /// /// An array of all methods enabled in this namespace /// [JsonProperty("methods")] - public string[] Methods { get; set; } + public string[] Methods; /// /// An array of all events enabled in this namespace /// [JsonProperty("events")] - public string[] Events { get; set; } + public string[] Events; public Namespace WithMethod(string method) { diff --git a/WalletConnectSharp.Sign/Models/Participant.cs b/WalletConnectSharp.Sign/Models/Participant.cs index 1073121..cc3a49d 100644 --- a/WalletConnectSharp.Sign/Models/Participant.cs +++ b/WalletConnectSharp.Sign/Models/Participant.cs @@ -15,12 +15,12 @@ public class Participant /// The public key of this participant, encoded as a hex string /// [JsonProperty("publicKey")] - public string PublicKey { get; set; } + public string PublicKey; /// /// The metadata for this participant /// [JsonProperty("metadata")] - public Metadata Metadata { get; set; } + public Metadata Metadata; } } diff --git a/WalletConnectSharp.Sign/Models/PendingRequestStruct.cs b/WalletConnectSharp.Sign/Models/PendingRequestStruct.cs index a71bbe1..9d81a42 100644 --- a/WalletConnectSharp.Sign/Models/PendingRequestStruct.cs +++ b/WalletConnectSharp.Sign/Models/PendingRequestStruct.cs @@ -5,9 +5,9 @@ namespace WalletConnectSharp.Sign.Models; public struct PendingRequestStruct : IKeyHolder { - public long Id { get; set; } + public long Id; - public string Topic { get; set; } + public string Topic; public long Key { @@ -20,5 +20,5 @@ public long Key // Specify object here, so we can store any type // We don't care about type-safety for these pending // requests - public SessionRequest Parameters { get; set; } + public SessionRequest Parameters; } diff --git a/WalletConnectSharp.Sign/Models/ProposalStruct.cs b/WalletConnectSharp.Sign/Models/ProposalStruct.cs index 787f4b9..940b1fe 100644 --- a/WalletConnectSharp.Sign/Models/ProposalStruct.cs +++ b/WalletConnectSharp.Sign/Models/ProposalStruct.cs @@ -22,7 +22,7 @@ public struct ProposalStruct : IKeyHolder /// The id of this proposal /// [JsonProperty("id")] - public long? Id { get; set; } + public long? Id; /// /// This is the key field, mapped to the Id. Implemented for @@ -41,43 +41,43 @@ public long Key /// When this proposal expires /// [JsonProperty("expiry")] - public long? Expiry { get; set; } + public long? Expiry; /// /// Relay protocol options for this proposal /// [JsonProperty("relays")] - public ProtocolOptions[] Relays { get; set; } + public ProtocolOptions[] Relays; /// /// The participant that created this proposal /// [JsonProperty("proposer")] - public Participant Proposer { get; set; } + public Participant Proposer; /// /// The required namespaces for this proposal requests /// [JsonProperty("requiredNamespaces")] - public RequiredNamespaces RequiredNamespaces { get; set; } + public RequiredNamespaces RequiredNamespaces; /// /// The optional namespaces for this proposal requests /// [JsonProperty("optionalNamespaces")] - public Dictionary OptionalNamespaces { get; set; } + public Dictionary OptionalNamespaces; /// /// Custom session properties for this proposal request /// [JsonProperty("sessionProperties")] - public Dictionary SessionProperties { get; set; } + public Dictionary SessionProperties; /// /// The pairing topic this proposal lives in /// [JsonProperty("pairingTopic")] - public string PairingTopic { get; set; } + public string PairingTopic; /// /// Approve this proposal with a single address and (optional) protocol options. The diff --git a/WalletConnectSharp.Sign/Models/ProposedNamespace.cs b/WalletConnectSharp.Sign/Models/ProposedNamespace.cs index 0f0d3ad..d6db3b3 100644 --- a/WalletConnectSharp.Sign/Models/ProposedNamespace.cs +++ b/WalletConnectSharp.Sign/Models/ProposedNamespace.cs @@ -13,13 +13,13 @@ public class ProposedNamespace /// A list of all chains that are required to be enabled in this namespace /// [JsonProperty("chains")] - public string[] Chains { get; set; } + public string[] Chains; /// /// A list of all methods that are required to be enabled in this namespace /// [JsonProperty("methods")] - public string[] Methods { get; set; } + public string[] Methods; /// /// A list of all events that are required to be enabled in this namespace diff --git a/WalletConnectSharp.Sign/Models/SessionStruct.cs b/WalletConnectSharp.Sign/Models/SessionStruct.cs index 297b7cc..98165b0 100644 --- a/WalletConnectSharp.Sign/Models/SessionStruct.cs +++ b/WalletConnectSharp.Sign/Models/SessionStruct.cs @@ -14,55 +14,55 @@ public struct SessionStruct : IKeyHolder /// The topic of this session /// [JsonProperty("topic")] - public string Topic { get; set; } + public string Topic; /// /// The relay protocol options this session is using /// [JsonProperty("relay")] - public ProtocolOptions Relay { get; set; } + public ProtocolOptions Relay; /// /// When this session expires /// [JsonProperty("expiry")] - public long? Expiry { get; set; } + public long? Expiry; /// /// Whether this session has been acknowledged or not /// [JsonProperty("acknowledged")] - public bool? Acknowledged { get; set; } + public bool? Acknowledged; /// /// The public key of the current controller for this session /// [JsonProperty("controller")] - public string Controller { get; set; } + public string Controller; /// /// The enabled namespaces this session uses /// [JsonProperty("namespaces")] - public Namespaces Namespaces { get; set; } + public Namespaces Namespaces; /// /// The required enabled namespaces this session uses /// [JsonProperty("requiredNamespaces")] - public RequiredNamespaces RequiredNamespaces { get; set; } + public RequiredNamespaces RequiredNamespaces; /// /// The data that represents ourselves in this session /// [JsonProperty("self")] - public Participant Self { get; set; } + public Participant Self; /// /// The data that represents the peer in this session /// [JsonProperty("peer")] - public Participant Peer { get; set; } + public Participant Peer; /// /// This is the key field, mapped to the Topic. Implemented for diff --git a/WalletConnectSharp.Sign/Models/SignClientOptions.cs b/WalletConnectSharp.Sign/Models/SignClientOptions.cs index 3899d46..3692289 100644 --- a/WalletConnectSharp.Sign/Models/SignClientOptions.cs +++ b/WalletConnectSharp.Sign/Models/SignClientOptions.cs @@ -17,12 +17,12 @@ public class SignClientOptions : CoreOptions /// left null, then a new Core module will be created and initialized /// [JsonProperty("core")] - public ICore Core { get; set; } + public ICore Core; /// /// The Metadata the should broadcast with /// [JsonProperty("metadata")] - public Metadata Metadata { get; set; } + public Metadata Metadata; } } diff --git a/WalletConnectSharp.Web3Wallet/Models/BaseEventArgs.cs b/WalletConnectSharp.Web3Wallet/Models/BaseEventArgs.cs index 34eeb23..f05c18d 100644 --- a/WalletConnectSharp.Web3Wallet/Models/BaseEventArgs.cs +++ b/WalletConnectSharp.Web3Wallet/Models/BaseEventArgs.cs @@ -5,7 +5,7 @@ namespace WalletConnectSharp.Web3Wallet.Models; public class BaseEventArgs : EventArgs { [JsonProperty("id")] - public long Id { get; set; } + public long Id; [JsonProperty("topic")] public string Topic { get; set; } diff --git a/WalletConnectSharp.Web3Wallet/Models/SessionRequestEventArgs.cs b/WalletConnectSharp.Web3Wallet/Models/SessionRequestEventArgs.cs index 4866eda..20623d2 100644 --- a/WalletConnectSharp.Web3Wallet/Models/SessionRequestEventArgs.cs +++ b/WalletConnectSharp.Web3Wallet/Models/SessionRequestEventArgs.cs @@ -7,5 +7,5 @@ namespace WalletConnectSharp.Web3Wallet.Models; public class SessionRequestEventArgs : BaseEventArgs> { [JsonProperty("verifyContext")] - public VerifiedContext VerifyContext { get; set; } + public VerifiedContext VerifyContext; } From 9ea795aea09b1e6499867d9a85ddb90a26f58e07 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Fri, 8 Sep 2023 12:22:48 -0400 Subject: [PATCH 29/36] chore: add more builder functions --- .../AuthClientTest.cs | 7 ++++ .../SignTests.cs | 18 ++++++++-- .../Internals/EngineTasks.cs | 2 +- WalletConnectSharp.Sign/Models/Namespace.cs | 33 ++++++++++++++++++ WalletConnectSharp.Sign/Models/Namespaces.cs | 34 +++++++++++++++++++ .../Models/ProposalStruct.cs | 9 ++--- .../Models/ProposedNamespace.cs | 5 +++ .../Models/RequiredNamespaces.cs | 6 ++++ 8 files changed, 104 insertions(+), 10 deletions(-) diff --git a/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs b/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs index 14d2a3a..db6a92f 100644 --- a/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs +++ b/Tests/WalletConnectSharp.Auth.Tests/AuthClientTest.cs @@ -139,6 +139,10 @@ async void OnPeerBOnAuthRequested(object sender, AuthRequest request) void OnPeerAOnAuthResponded(object sender, AuthResponse args) { + var sessionTopic = args.Topic; + var cacao = args.Response.Result; + var signature = cacao.Signature; + Console.WriteLine($"{sessionTopic}: {signature}"); responses.Add(args); responseTask.SetResult(args); } @@ -147,6 +151,9 @@ void OnPeerAOnAuthResponded(object sender, AuthResponse args) void OnPeerAOnAuthError(object sender, AuthErrorResponse args) { + var sessionTopic = args.Topic; + var error = args.Error; + Console.WriteLine($"{sessionTopic}: {error}"); responses.Add(args); responseTask.SetResult(args); } diff --git a/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs b/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs index f6e600e..844f577 100644 --- a/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs +++ b/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs @@ -62,9 +62,11 @@ public class ChainChangedEvent { { "eip155", new ProposedNamespace() - .WithMethod("eth_signTransaction") - .WithChain("eip155:1") - .WithEvent("chainChanged") + { + Chains = new []{ "eip155:1" }, + Methods = new[] { "eth_signTransaction" }, + Events = new[] { "chainChanged" } + } } }; @@ -214,8 +216,18 @@ public async void TestShouldRejectSessionProposal() _wallet.On(EngineEvents.SessionProposal, async (sender, @event) => { var proposal = @event.EventData.Proposal; + + await _wallet.ApproveSession(proposal, "eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb"); + + var requiredNamespaces = proposal.RequiredNamespaces; + var approvedNamespaces = new Namespaces(requiredNamespaces); + approvedNamespaces["eip155"].WithAccount("eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb"); + + await _wallet.ApproveSession(proposal.Id, approvedNamespaces); + var id = @event.EventData.Id; Assert.Equal(TestRequiredNamespaces, proposal.RequiredNamespaces); + await _wallet.RejectSession(id, rejectionError); task1.TrySetResult(true); }); diff --git a/WalletConnectSharp.Sign/Internals/EngineTasks.cs b/WalletConnectSharp.Sign/Internals/EngineTasks.cs index cabb0f4..dd3e445 100644 --- a/WalletConnectSharp.Sign/Internals/EngineTasks.cs +++ b/WalletConnectSharp.Sign/Internals/EngineTasks.cs @@ -90,7 +90,7 @@ async Task IEnginePrivate.SetProposal(long id, ProposalStruct proposal) Task IEnginePrivate.Cleanup() { List sessionTopics = (from session in this.Client.Session.Values.Where(e => e.Expiry != null) where Clock.IsExpired(session.Expiry.Value) select session.Topic).ToList(); - List proposalIds = (from p in this.Client.Proposal.Values.Where(e => e.Expiry != null) where Clock.IsExpired(p.Expiry.Value) select p.Id.Value).ToList(); + List proposalIds = (from p in this.Client.Proposal.Values.Where(e => e.Expiry != null) where Clock.IsExpired(p.Expiry.Value) select p.Id).ToList(); return Task.WhenAll( sessionTopics.Select(t => PrivateThis.DeleteSession(t)).Concat( diff --git a/WalletConnectSharp.Sign/Models/Namespace.cs b/WalletConnectSharp.Sign/Models/Namespace.cs index 1a2545e..49d0759 100644 --- a/WalletConnectSharp.Sign/Models/Namespace.cs +++ b/WalletConnectSharp.Sign/Models/Namespace.cs @@ -8,6 +8,15 @@ namespace WalletConnectSharp.Sign.Models /// public class Namespace { + public Namespace(ProposedNamespace proposedNamespace) + { + this.Methods = proposedNamespace.Methods; + this.Chains = proposedNamespace.Chains; + this.Events = proposedNamespace.Events; + } + + public Namespace() { } + /// /// An array of all accounts enabled in this namespace /// @@ -25,6 +34,12 @@ public class Namespace /// [JsonProperty("events")] public string[] Events { get; set; } + + /// + /// An array of all chains enabled in this namespace + /// + [JsonProperty("chains")] + public string[] Chains { get; set; } public Namespace WithMethod(string method) { @@ -32,6 +47,24 @@ public Namespace WithMethod(string method) return this; } + public Namespace WithChain(string chain) + { + Chains = Chains.Append(chain).ToArray(); + return this; + } + + public Namespace WithEvent(string @event) + { + Events = Events.Append(@event).ToArray(); + return this; + } + + public Namespace WithAccount(string account) + { + Accounts = Accounts.Append(account).ToArray(); + return this; + } + protected bool Equals(Namespace other) { return Equals(Accounts, other.Accounts) && Equals(Methods, other.Methods) && Equals(Events, other.Events); diff --git a/WalletConnectSharp.Sign/Models/Namespaces.cs b/WalletConnectSharp.Sign/Models/Namespaces.cs index a079ae5..a154d64 100644 --- a/WalletConnectSharp.Sign/Models/Namespaces.cs +++ b/WalletConnectSharp.Sign/Models/Namespaces.cs @@ -19,6 +19,16 @@ public Namespaces(Namespaces namespaces) : base(namespaces) } + public Namespaces(RequiredNamespaces requiredNamespaces) + { + WithProposedNamespaces(requiredNamespaces); + } + + public Namespaces(Dictionary proposedNamespaces) + { + WithProposedNamespaces(proposedNamespaces); + } + public bool Equals(Namespaces other) { return new DictionaryComparer(Namespace.NamespaceComparer).Equals(this, other); @@ -48,5 +58,29 @@ public override int GetHashCode() { throw new NotImplementedException(); } + + public Namespaces WithNamespace(string chainNamespace, Namespace nm) + { + Add(chainNamespace, nm); + return this; + } + + public Namespace At(string chainNamespace) + { + return this[chainNamespace]; + } + + public Namespaces WithProposedNamespaces(Dictionary proposedNamespaces) + { + foreach (var pair in proposedNamespaces) + { + var chainNamespace = pair.Key; + var requiredNamespace = pair.Value; + + Add(chainNamespace, new Namespace(requiredNamespace)); + } + + return this; + } } } diff --git a/WalletConnectSharp.Sign/Models/ProposalStruct.cs b/WalletConnectSharp.Sign/Models/ProposalStruct.cs index 787f4b9..fe2fa37 100644 --- a/WalletConnectSharp.Sign/Models/ProposalStruct.cs +++ b/WalletConnectSharp.Sign/Models/ProposalStruct.cs @@ -22,7 +22,7 @@ public struct ProposalStruct : IKeyHolder /// The id of this proposal /// [JsonProperty("id")] - public long? Id { get; set; } + public long Id { get; set; } /// /// This is the key field, mapped to the Id. Implemented for @@ -143,7 +143,7 @@ public ApproveParams ApproveProposal(string[] approvedAccounts, ProtocolOptions return new ApproveParams() { - Id = Id.Value, + Id = Id, RelayProtocol = relayProtocol, Namespaces = namespaces, SessionProperties = SessionProperties, @@ -162,7 +162,7 @@ public RejectParams RejectProposal(Error error) if (Id == null) throw new Exception("Proposal has no set Id"); - return new RejectParams() {Id = Id.Value, Reason = error}; + return new RejectParams() {Id = Id, Reason = error}; } /// @@ -174,9 +174,6 @@ public RejectParams RejectProposal(Error error) /// If this proposal has no Id public RejectParams RejectProposal(string message = null) { - if (Id == null) - throw new Exception("Proposal has no set Id"); - if (message == null) message = "Proposal denied by remote host"; diff --git a/WalletConnectSharp.Sign/Models/ProposedNamespace.cs b/WalletConnectSharp.Sign/Models/ProposedNamespace.cs index 0f0d3ad..8248d19 100644 --- a/WalletConnectSharp.Sign/Models/ProposedNamespace.cs +++ b/WalletConnectSharp.Sign/Models/ProposedNamespace.cs @@ -70,6 +70,11 @@ public ProposedNamespace WithEvent(string @event) return this; } + public Namespace WithAccount(string account) + { + return new Namespace(this).WithAccount(account); + } + protected bool Equals(ProposedNamespace other) { return Equals(Chains, other.Chains) && Equals(Methods, other.Methods) && Equals(Events, other.Events); diff --git a/WalletConnectSharp.Sign/Models/RequiredNamespaces.cs b/WalletConnectSharp.Sign/Models/RequiredNamespaces.cs index 8a6c2d0..d093d17 100644 --- a/WalletConnectSharp.Sign/Models/RequiredNamespaces.cs +++ b/WalletConnectSharp.Sign/Models/RequiredNamespaces.cs @@ -52,5 +52,11 @@ public int GetHashCode(RequiredNamespaces obj) { throw new NotImplementedException(); } + + public RequiredNamespaces WithProposedNamespace(string chainNamespace, ProposedNamespace proposedNamespace) + { + Add(chainNamespace, proposedNamespace); + return this; + } } } From 250ec1eeb778ad3b9eae492aca045d0e9dba583e Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Fri, 8 Sep 2023 12:26:49 -0400 Subject: [PATCH 30/36] chore: log errors when loading files --- Core Modules/WalletConnectSharp.Storage/FileSystemStorage.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core Modules/WalletConnectSharp.Storage/FileSystemStorage.cs b/Core Modules/WalletConnectSharp.Storage/FileSystemStorage.cs index 58fda7d..2376614 100644 --- a/Core Modules/WalletConnectSharp.Storage/FileSystemStorage.cs +++ b/Core Modules/WalletConnectSharp.Storage/FileSystemStorage.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; +using WalletConnectSharp.Common.Logging; namespace WalletConnectSharp.Storage { @@ -119,6 +120,8 @@ private async Task Load() { // Move the file to a .unsupported file // and start fresh + WCLogger.LogError(e); + WCLogger.LogError("Cannot load JSON file, moving data to .unsupported file to force continue"); if (File.Exists(FilePath + ".unsupported")) File.Move(FilePath + ".unsupported", FilePath + "." + Guid.NewGuid() + ".unsupported"); File.Move(FilePath, FilePath + ".unsupported"); From 57c884c5f0f78d909b9df7aed20b231507eb9710 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Sat, 9 Sep 2023 15:15:12 -0400 Subject: [PATCH 31/36] refactor: change validation string to enum --- .../Models/Verify/Validation.cs | 8 ++-- .../Models/Verify/VerifiedContext.cs | 37 ++++++++++++++++--- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/WalletConnectSharp.Core/Models/Verify/Validation.cs b/WalletConnectSharp.Core/Models/Verify/Validation.cs index cab41aa..cb5fa84 100644 --- a/WalletConnectSharp.Core/Models/Verify/Validation.cs +++ b/WalletConnectSharp.Core/Models/Verify/Validation.cs @@ -1,8 +1,8 @@ namespace WalletConnectSharp.Core.Models.Verify; -public static class Validation +public enum Validation { - public const string Unknown = "UNKNOWN"; - public const string Valid = "VALID"; - public const string Invalid = "INVALID"; + Unknown, + Valid, + Invalid, } diff --git a/WalletConnectSharp.Core/Models/Verify/VerifiedContext.cs b/WalletConnectSharp.Core/Models/Verify/VerifiedContext.cs index eb8d593..f130a75 100644 --- a/WalletConnectSharp.Core/Models/Verify/VerifiedContext.cs +++ b/WalletConnectSharp.Core/Models/Verify/VerifiedContext.cs @@ -10,22 +10,47 @@ public class VerifiedContext [JsonProperty("validation")] private string _validation; - public string Validation + public string ValidationString => _validation; + + public Validation Validation { get { - return _validation; + return FromString(); } set { - if (value != Verify.Validation.Unknown && value != Verify.Validation.Invalid && - value != Verify.Validation.Valid) - throw new ArgumentException("Invalid validation value, must be one of Verify.Validation string"); - _validation = value; + _validation = AsString(value); } } [JsonProperty("verifyUrl")] public string VerifyUrl { get; set; } + + private Validation FromString() + { + switch (ValidationString.ToLowerInvariant()) + { + case "VALID": + return Validation.Valid; + case "INVALID": + return Validation.Invalid; + default: + return Validation.Unknown; + } + } + + private string AsString(Validation str) + { + switch (str) + { + case Validation.Invalid: + return "INVALID"; + case Validation.Valid: + return "VALID"; + default: + return "UNKNOWN"; + } + } } From 96d267cb33cef65b85f28a22320d75a14fe05d07 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Sun, 10 Sep 2023 00:10:43 -0400 Subject: [PATCH 32/36] fix: mobile unity issues --- .../Logging/WCLogger.cs | 5 ++++ .../Logging/WrapperLogger.cs | 28 ++++++++++++++++++ .../WebsocketConnection.cs | 6 ++++ .../Interfaces/IJsonRpcConnection.cs | 4 ++- .../JsonRpcProvider.cs | 3 +- .../Controllers/Publisher.cs | 2 +- .../Controllers/Relayer.cs | 15 ++++++++-- .../Controllers/Subscriber.cs | 20 ++++++++++--- WalletConnectSharp.Sign/Engine.cs | 21 ++++++++++++-- .../Internals/EngineHandler.cs | 29 ++++++++++++++++++- 10 files changed, 121 insertions(+), 12 deletions(-) create mode 100644 Core Modules/WalletConnectSharp.Common/Logging/WrapperLogger.cs diff --git a/Core Modules/WalletConnectSharp.Common/Logging/WCLogger.cs b/Core Modules/WalletConnectSharp.Common/Logging/WCLogger.cs index a5ae071..271e5e9 100644 --- a/Core Modules/WalletConnectSharp.Common/Logging/WCLogger.cs +++ b/Core Modules/WalletConnectSharp.Common/Logging/WCLogger.cs @@ -4,6 +4,11 @@ public class WCLogger { public static ILogger Logger; + public static ILogger WithContext(string context) + { + return new WrapperLogger(Logger, context); + } + public static void Log(string message) { if (Logger == null) diff --git a/Core Modules/WalletConnectSharp.Common/Logging/WrapperLogger.cs b/Core Modules/WalletConnectSharp.Common/Logging/WrapperLogger.cs new file mode 100644 index 0000000..a48842b --- /dev/null +++ b/Core Modules/WalletConnectSharp.Common/Logging/WrapperLogger.cs @@ -0,0 +1,28 @@ +namespace WalletConnectSharp.Common.Logging; + +public class WrapperLogger : ILogger +{ + private ILogger _logger; + private string prefix; + + public WrapperLogger(ILogger logger, string prefix) + { + _logger = logger; + this.prefix = prefix; + } + + public void Log(string message) + { + _logger.Log($"[{prefix}] {message}"); + } + + public void LogError(string message) + { + _logger.LogError($"[{prefix}] {message}"); + } + + public void LogError(Exception e) + { + _logger.LogError(e); + } +} diff --git a/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnection.cs b/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnection.cs index ea819b7..487a631 100644 --- a/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnection.cs +++ b/Core Modules/WalletConnectSharp.Network.Websocket/WebsocketConnection.cs @@ -36,6 +36,12 @@ public string Url } } + public bool IsPaused + { + get; + internal set; + } + /// /// The name of this websocket connection module /// diff --git a/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcConnection.cs b/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcConnection.cs index 8693d69..33d72a5 100644 --- a/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcConnection.cs +++ b/Core Modules/WalletConnectSharp.Network/Interfaces/IJsonRpcConnection.cs @@ -24,6 +24,8 @@ public interface IJsonRpcConnection : IEvents, IDisposable /// string Url { get; } + bool IsPaused { get; } + /// /// Open this connection /// @@ -74,4 +76,4 @@ public interface IJsonRpcConnection : IEvents, IDisposable /// A task that is performing the send Task SendError(IJsonRpcError errorPayload, object context); } -} \ No newline at end of file +} diff --git a/Core Modules/WalletConnectSharp.Network/JsonRpcProvider.cs b/Core Modules/WalletConnectSharp.Network/JsonRpcProvider.cs index 72ba26e..9082c96 100644 --- a/Core Modules/WalletConnectSharp.Network/JsonRpcProvider.cs +++ b/Core Modules/WalletConnectSharp.Network/JsonRpcProvider.cs @@ -119,9 +119,10 @@ public async Task Connect(string connection) /// The connection object to use to connect public async Task Connect(IJsonRpcConnection connection) { - if (this._connection == connection && connection.Connected) return; + if (this._connection.Url == connection.Url && connection.Connected) return; if (this._connection.Connected) { + WCLogger.Log("Current connection still open, closing connection"); await this._connection.Close(); } diff --git a/WalletConnectSharp.Core/Controllers/Publisher.cs b/WalletConnectSharp.Core/Controllers/Publisher.cs index 84268fb..8f28a93 100644 --- a/WalletConnectSharp.Core/Controllers/Publisher.cs +++ b/WalletConnectSharp.Core/Controllers/Publisher.cs @@ -147,7 +147,7 @@ public async Task Publish(string topic, string message, PublishOptions opts = nu try { await RpcPublish(topic, message, @params.Options.TTL, @params.Options.Tag, @params.Options.Relay) - .WithTimeout(TimeSpan.FromSeconds(10)); + .WithTimeout(TimeSpan.FromSeconds(45)); this.Relayer.Events.Trigger(RelayerEvents.Publish, @params); OnPublish(hash); } diff --git a/WalletConnectSharp.Core/Controllers/Relayer.cs b/WalletConnectSharp.Core/Controllers/Relayer.cs index 65276dd..e586fa5 100644 --- a/WalletConnectSharp.Core/Controllers/Relayer.cs +++ b/WalletConnectSharp.Core/Controllers/Relayer.cs @@ -236,6 +236,9 @@ protected virtual void RegisterEventListeners() { this.Events.ListenFor(RelayerEvents.ConnectionStalled, async (sender, @event) => { + if (this.Provider.Connection.IsPaused) + return; + await this.RestartTransport(); }); } @@ -457,14 +460,22 @@ protected virtual void IsInitialized() private async Task ToEstablishConnection() { - if (Connected) return; + if (Connected) + { + while (Provider.Connection.IsPaused) + { + WCLogger.Log("[Relayer] Waiting for connection to unpause"); + await Task.Delay(2); + } + return; + } if (Connecting) { // Check for connection while (Connecting) { WCLogger.Log("[Relayer] Waiting for connection to open"); - await Task.Delay(20); + await Task.Delay(2); } if (!Connected && !Connecting) diff --git a/WalletConnectSharp.Core/Controllers/Subscriber.cs b/WalletConnectSharp.Core/Controllers/Subscriber.cs index f6c9821..a3d527d 100644 --- a/WalletConnectSharp.Core/Controllers/Subscriber.cs +++ b/WalletConnectSharp.Core/Controllers/Subscriber.cs @@ -1,3 +1,4 @@ +using WalletConnectSharp.Common.Logging; using WalletConnectSharp.Common.Model.Errors; using WalletConnectSharp.Common.Model.Relay; using WalletConnectSharp.Common.Utils; @@ -73,6 +74,8 @@ public string Version private Dictionary pending = new Dictionary(); private TaskCompletionSource restartTask = null; + private event EventHandler onSubscriberReady; + public bool RestartInProgress { get @@ -163,6 +166,7 @@ public string StorageKey private IRelayer _relayer; private bool initialized; private string clientId; + private ILogger logger; private ActiveSubscription[] cached = Array.Empty(); /// @@ -174,6 +178,8 @@ public Subscriber(IRelayer relayer) _relayer = relayer; Events = new EventDelegator(this); + + logger = WCLogger.WithContext(Context); } /// @@ -341,6 +347,9 @@ protected virtual void OnEnabled() { cached = Array.Empty(); initialized = true; + + if (onSubscriberReady != null) + onSubscriberReady(this, EventArgs.Empty); } protected virtual void OnDisconnect() @@ -350,6 +359,7 @@ protected virtual void OnDisconnect() protected virtual void OnDisable() { + logger.Log("OnDisable invoked"); cached = Values; _subscriptions.Clear(); _topicMap.Clear(); @@ -364,11 +374,13 @@ protected virtual async void OnConnect() OnEnabled(); } - private Task RestartToComplete() + private async Task RestartToComplete() { - if (!RestartInProgress) return Task.CompletedTask; + if (!RestartInProgress) return; - return restartTask.Task; + logger.Log("waiting for restart"); + await restartTask.Task; + logger.Log("restart completed"); } protected virtual void OnSubscribe(string id, PendingSubscription @params) @@ -610,7 +622,7 @@ protected virtual async Task RpcBatchSubscribe(string[] topics, Protoc try { return await this._relayer.Request(request) - .WithTimeout(TimeSpan.FromSeconds(10)); + .WithTimeout(TimeSpan.FromSeconds(45)); } catch (Exception e) { diff --git a/WalletConnectSharp.Sign/Engine.cs b/WalletConnectSharp.Sign/Engine.cs index e3d0d33..9dd9fb1 100644 --- a/WalletConnectSharp.Sign/Engine.cs +++ b/WalletConnectSharp.Sign/Engine.cs @@ -63,6 +63,8 @@ public string Context } } + private ILogger logger { get; } + /// /// Create a new Engine with the given module /// @@ -71,6 +73,8 @@ public Engine(ISignClient client) { this.Client = client; Events = new EventDelegator(this); + + logger = WCLogger.WithContext(Context); } /// @@ -256,9 +260,13 @@ public async Task Connect(ConnectOptions options) TaskCompletionSource approvalTask = new TaskCompletionSource(); this.Events.ListenForOnce("session_connect", async (sender, e) => { + logger.Log("Got session_connect event for session struct"); if (approvalTask.Task.IsCompleted) + { + logger.Log("approval already received though, skipping"); return; - + } + var session = e.EventData; session.Self.PublicKey = publicKey; var completeSession = session with { RequiredNamespaces = requiredNamespaces }; @@ -274,10 +282,16 @@ public async Task Connect(ConnectOptions options) this.Events.ListenForOnce>("session_connect", (sender, e) => { + logger.Log("Got session_connect event for rpc response"); if (approvalTask.Task.IsCompleted) + { + logger.Log("approval already received though, skipping"); return; + } + if (e.EventData.IsError) { + logger.LogError("Got session_connect error " + e.EventData.Error.Message); approvalTask.SetException(e.EventData.Error.ToException()); } }); @@ -287,9 +301,12 @@ public async Task Connect(ConnectOptions options) throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY, $"connect() pairing topic: {topic}"); } - WCLogger.Log($"Sending request JSON {JsonConvert.SerializeObject(proposal)} to topic {topic}"); + logger.Log($"Sending request JSON {JsonConvert.SerializeObject(proposal)} to topic {topic}"); var id = await MessageHandler.SendRequest(topic, proposal); + + logger.Log($"Got back {id} as request pending id"); + var expiry = Clock.CalculateExpiry(Clock.FIVE_MINUTES); await PrivateThis.SetProposal(id, new ProposalStruct() diff --git a/WalletConnectSharp.Sign/Internals/EngineHandler.cs b/WalletConnectSharp.Sign/Internals/EngineHandler.cs index cf36b5b..a8c2cb5 100644 --- a/WalletConnectSharp.Sign/Internals/EngineHandler.cs +++ b/WalletConnectSharp.Sign/Internals/EngineHandler.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using WalletConnectSharp.Common.Logging; using WalletConnectSharp.Common.Model.Errors; using WalletConnectSharp.Common.Utils; using WalletConnectSharp.Core.Models.Expirer; @@ -81,13 +82,16 @@ await MessageHandler.SendError(id, topic async Task IEnginePrivate.OnSessionProposeResponse(string topic, JsonRpcResponse payload) { var id = payload.Id; + logger.Log($"Got session propose response with id {id}"); if (payload.IsError) { + logger.LogError("response was error"); await this.Client.Proposal.Delete(id, Error.FromErrorType(ErrorType.USER_DISCONNECTED)); this.Events.Trigger(EngineEvents.SessionConnect, payload); } else { + logger.Log("response was success"); var result = payload.Result; var proposal = this.Client.Proposal.Get(id); var selfPublicKey = proposal.Proposer.PublicKey; @@ -97,8 +101,28 @@ async Task IEnginePrivate.OnSessionProposeResponse(string topic, JsonRpcResponse selfPublicKey, peerPublicKey ); - var subscriptionId = await this.Client.Core.Relayer.Subscribe(sessionTopic); await this.Client.Core.Pairing.Activate(topic); + logger.Log($"pairing activated for topic {topic}"); + + // try to do this a couple of times .. do it until it works? + int attempts = 5; + do + { + try + { + var subscriptionId = await this.Client.Core.Relayer.Subscribe(sessionTopic); + return; + } + catch (Exception e) + { + WCLogger.LogError($"Got error subscribing to topic, attempts left: {attempts}"); + WCLogger.LogError(e); + attempts--; + await Task.Yield(); + } + } while (attempts > 0); + + throw new IOException($"Could not subscribe to session topic {sessionTopic}"); } } @@ -106,6 +130,7 @@ async Task IEnginePrivate.OnSessionSettleRequest(string topic, JsonRpcRequest(id, topic, Error.FromException(e)); } } From 223b9f0b10a7ec3bbeb2b54abd55bb524826c4d9 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Sun, 10 Sep 2023 13:47:45 -0400 Subject: [PATCH 33/36] fix: wrapper logger being null --- .../WalletConnectSharp.Common/Logging/WrapperLogger.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core Modules/WalletConnectSharp.Common/Logging/WrapperLogger.cs b/Core Modules/WalletConnectSharp.Common/Logging/WrapperLogger.cs index a48842b..5e86f96 100644 --- a/Core Modules/WalletConnectSharp.Common/Logging/WrapperLogger.cs +++ b/Core Modules/WalletConnectSharp.Common/Logging/WrapperLogger.cs @@ -13,16 +13,19 @@ public WrapperLogger(ILogger logger, string prefix) public void Log(string message) { + if (_logger == null) return; _logger.Log($"[{prefix}] {message}"); } public void LogError(string message) { + if (_logger == null) return; _logger.LogError($"[{prefix}] {message}"); } public void LogError(Exception e) { + if (_logger == null) return; _logger.LogError(e); } } From d933f9846212419ed7077678af6c5cc8d2aa162b Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Sun, 10 Sep 2023 13:48:06 -0400 Subject: [PATCH 34/36] fix: store module update function --- WalletConnectSharp.Core/Controllers/Store.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/WalletConnectSharp.Core/Controllers/Store.cs b/WalletConnectSharp.Core/Controllers/Store.cs index 18dce7b..cb58900 100644 --- a/WalletConnectSharp.Core/Controllers/Store.cs +++ b/WalletConnectSharp.Core/Controllers/Store.cs @@ -206,6 +206,22 @@ public Task Update(TKey key, TValue update) } } + var fields = t.GetFields(); + + // Loop through all of them + foreach (var prop in fields) + { + // Grab the updated value + var @value = prop.GetValue(update); + // If it exists (its not null), then set it + if (@value != null) + { + object test = previousValue; + prop.SetValue(test, @value); + previousValue = (TValue)test; + } + } + // Now, set the update variable to be the new modified // previousValue object update = previousValue; From 79211add4864c846b9ad16f409613f1bd2f40cb4 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Sun, 10 Sep 2023 13:59:45 -0400 Subject: [PATCH 35/36] fix: more unit tests, get rid of asdictionary --- .../Model/Errors/SdkErrors.cs | 10 +++---- .../Model/Errors/WalletConnectException.cs | 6 ++-- .../Utils/Extensions.cs | 28 ------------------- .../WalletConnectSharp.Crypto/Crypto.cs | 5 +++- .../WalletConnectSharp.Crypto/KeyChain.cs | 15 ++++++++-- .../Models/Error.cs | 13 ++++++++- .../Controllers/AuthEngine.cs | 4 +-- .../Controllers/JsonRpcHistory.cs | 5 +++- 8 files changed, 42 insertions(+), 44 deletions(-) diff --git a/Core Modules/WalletConnectSharp.Common/Model/Errors/SdkErrors.cs b/Core Modules/WalletConnectSharp.Common/Model/Errors/SdkErrors.cs index 5133bd4..db44bf6 100644 --- a/Core Modules/WalletConnectSharp.Common/Model/Errors/SdkErrors.cs +++ b/Core Modules/WalletConnectSharp.Common/Model/Errors/SdkErrors.cs @@ -27,9 +27,9 @@ public static class SdkErrors /// 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, object @params = null) + public static string MessageFromType(ErrorType type, Dictionary @params = null) { - return MessageFromType(type, null, @params.AsDictionary()); + return MessageFromType(type, null, @params); } /// @@ -47,8 +47,8 @@ public static string MessageFromType(ErrorType type, string message = null, Dict @params = new Dictionary(); } - if (!string.IsNullOrWhiteSpace(message) && !@params.ContainsKey("message")) - @params.Add("message", message); + if (!string.IsNullOrWhiteSpace(message)) + @params.TryAdd("message", message); string errorMessage; switch (type) @@ -231,4 +231,4 @@ private static string FormatErrorText(string formattedText, DictionaryThe error type of the exception /// Additional (optional) parameters for the generated error message /// A new exception - public static WalletConnectException FromType(ErrorType type, object @params = null) + public static WalletConnectException FromType(ErrorType type, Dictionary @params = null) { - return FromType(type, null, @params.AsDictionary()); + return FromType(type, null, @params); } } -} \ No newline at end of file +} diff --git a/Core Modules/WalletConnectSharp.Common/Utils/Extensions.cs b/Core Modules/WalletConnectSharp.Common/Utils/Extensions.cs index 63b210e..e7ae344 100644 --- a/Core Modules/WalletConnectSharp.Common/Utils/Extensions.cs +++ b/Core Modules/WalletConnectSharp.Common/Utils/Extensions.cs @@ -11,34 +11,6 @@ namespace WalletConnectSharp.Common.Utils /// public static class Extensions { - /// - /// Convert an anonymous type to a Dictionary - /// - /// The anonymous type instance to convert to a dictionary - /// Enforce all keys to be lowercased - /// A dictionary where each key is the property name of the anonymous type - /// and each value is the property's value - public static Dictionary AsDictionary(this object obj, bool enforceLowercase = true) - { - if (obj is Dictionary objects) - return objects; - - var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); - - if (obj != null) - { - foreach (PropertyInfo propertyDescriptor in obj.GetType().GetProperties()) - { - object value = propertyDescriptor.GetValue(obj, null); - var key = enforceLowercase ? propertyDescriptor.Name.ToLower() : propertyDescriptor.Name; - - dict.Add(key, value); - } - } - - return dict; - } - /// /// Returns true if the given object is a numeric type /// diff --git a/Core Modules/WalletConnectSharp.Crypto/Crypto.cs b/Core Modules/WalletConnectSharp.Crypto/Crypto.cs index 537c8a7..6b1ecc8 100644 --- a/Core Modules/WalletConnectSharp.Crypto/Crypto.cs +++ b/Core Modules/WalletConnectSharp.Crypto/Crypto.cs @@ -578,7 +578,10 @@ private void IsInitialized() { if (!this._initialized) { - throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, new {Name}); + throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, new Dictionary() + { + { "Name", Name } + }); } } diff --git a/Core Modules/WalletConnectSharp.Crypto/KeyChain.cs b/Core Modules/WalletConnectSharp.Crypto/KeyChain.cs index c3671af..511b57a 100644 --- a/Core Modules/WalletConnectSharp.Crypto/KeyChain.cs +++ b/Core Modules/WalletConnectSharp.Crypto/KeyChain.cs @@ -142,7 +142,10 @@ public async Task Get(string tag) if (!await Has(tag)) { - throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY, new {tag}); + throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY, new Dictionary() + { + {"tag", tag} + }); } return this._keyChain[tag]; @@ -160,7 +163,10 @@ public async Task Delete(string tag) if (!await Has(tag)) { - throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY, new {tag}); + throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY, new Dictionary() + { + {"tag", tag} + }); } _keyChain.Remove(tag); @@ -172,7 +178,10 @@ private void IsInitialized() { if (!this._initialized) { - throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, new {Name}); + throw WalletConnectException.FromType(ErrorType.NOT_INITIALIZED, new Dictionary() + { + {"Name", Name} + }); } } diff --git a/Core Modules/WalletConnectSharp.Network/Models/Error.cs b/Core Modules/WalletConnectSharp.Network/Models/Error.cs index 2f4b699..aa68838 100644 --- a/Core Modules/WalletConnectSharp.Network/Models/Error.cs +++ b/Core Modules/WalletConnectSharp.Network/Models/Error.cs @@ -28,6 +28,17 @@ 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 @@ -36,7 +47,7 @@ public class Error /// Extra parameters for the error message /// Extra data that is stored in the Data field of the newly created ErrorResponse /// A new ErrorResponse - public static Error FromErrorType(ErrorType type, object @params = null, string extraData = null) + public static Error FromErrorType(ErrorType type, Dictionary @params = null, string extraData = null) { string message = SdkErrors.MessageFromType(type, @params); diff --git a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs index 2fcdb53..94efde1 100644 --- a/WalletConnectSharp.Auth/Controllers/AuthEngine.cs +++ b/WalletConnectSharp.Auth/Controllers/AuthEngine.cs @@ -280,9 +280,9 @@ private async Task OnAuthResponse(string topic, JsonRpcResponse response) { this.Client.OnAuthResponse(new AuthErrorResponse() { - Id = id, Topic = topic, Error = Error.FromErrorType(ErrorType.GENERIC, new + Id = id, Topic = topic, Error = Error.FromErrorType(ErrorType.GENERIC, new Dictionary() { - Message = "Invalid signature" + {"Message", "Invalid signature"} }) }); } diff --git a/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs b/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs index 552954c..9385b64 100644 --- a/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs +++ b/WalletConnectSharp.Core/Controllers/JsonRpcHistory.cs @@ -272,7 +272,10 @@ private JsonRpcRecord GetRecord(long id) if (!_records.ContainsKey(id)) { - throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY, new {Tag = $"{Name}: {id}"}); + throw WalletConnectException.FromType(ErrorType.NO_MATCHING_KEY, new Dictionary() + { + {"Tag", $"{Name}: {id}"} + }); } return _records[id]; From ba6b2c07f260d4cc138c4469964c2b5038345318 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Sun, 10 Sep 2023 14:08:38 -0400 Subject: [PATCH 36/36] fix: unit tests --- Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs b/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs index 323cd25..4ffa579 100644 --- a/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs +++ b/Tests/WalletConnectSharp.Web3Wallet.Tests/SignTests.cs @@ -217,14 +217,6 @@ public async void TestShouldRejectSessionProposal() { var proposal = @event.EventData.Proposal; - await _wallet.ApproveSession(proposal, "eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb"); - - var requiredNamespaces = proposal.RequiredNamespaces; - var approvedNamespaces = new Namespaces(requiredNamespaces); - approvedNamespaces["eip155"].WithAccount("eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb"); - - await _wallet.ApproveSession(proposal.Id, approvedNamespaces); - var id = @event.EventData.Id; Assert.Equal(TestRequiredNamespaces, proposal.RequiredNamespaces);