From 9dbd3fb3974e746ab0149ccae20ee4ac9a514ca5 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Wed, 1 Nov 2023 00:14:42 -0400 Subject: [PATCH 1/3] feat: created address api to set defaults and fetch connected address --- .../WalletConnectSharp.Sign.Test/SignTests.cs | 175 +++++++++++++++++ .../Controllers/AddressProvider.cs | 185 ++++++++++++++++++ WalletConnectSharp.Sign/Engine.cs | 39 +++- .../Interfaces/IAddressProvider.cs | 21 ++ .../Interfaces/IEngineAPI.cs | 63 ++++++ .../Interfaces/ISignClient.cs | 6 + .../Internals/EngineHandler.cs | 3 +- .../Internals/EngineValidation.cs | 2 +- .../Models/Caip25Address.cs | 7 + .../Models/RequiredNamespaces.cs | 16 ++ .../WalletConnectSignClient.cs | 40 +++- 11 files changed, 553 insertions(+), 4 deletions(-) create mode 100644 WalletConnectSharp.Sign/Controllers/AddressProvider.cs create mode 100644 WalletConnectSharp.Sign/Interfaces/IAddressProvider.cs create mode 100644 WalletConnectSharp.Sign/Models/Caip25Address.cs diff --git a/Tests/WalletConnectSharp.Sign.Test/SignTests.cs b/Tests/WalletConnectSharp.Sign.Test/SignTests.cs index f739761..c3cc145 100644 --- a/Tests/WalletConnectSharp.Sign.Test/SignTests.cs +++ b/Tests/WalletConnectSharp.Sign.Test/SignTests.cs @@ -382,5 +382,180 @@ public async void TestTwoUniqueSessionRequestResponse() Assert.True(responseReturned2); } + + [Fact, Trait("Category", "integration")] + public async void TestTwoUniqueSessionRequestResponseUsingAddressProviderDefaults() + { + 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(testData); + var responseReturned2 = await dappClient.Engine.Request(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); + } + + [Fact, Trait("Category", "integration")] + public async void TestAddressProviderDefaults() + { + 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); + + await connectData.Approval; + await approveData.Acknowledged(); + + var address = dappClient.AddressProvider.CurrentAddress(); + Assert.Equal(testAddress, address.Address); + Assert.Equal("eip155:1", address.ChainId); + Assert.Equal("eip155:1", dappClient.AddressProvider.DefaultChain); + Assert.Equal("eip155", dappClient.AddressProvider.DefaultNamespace); + + address = walletClient.AddressProvider.CurrentAddress(); + Assert.Equal(testAddress, address.Address); + Assert.Equal("eip155:1", address.ChainId); + Assert.Equal("eip155:1", dappClient.AddressProvider.DefaultChain); + Assert.Equal("eip155", dappClient.AddressProvider.DefaultNamespace); + } } } diff --git a/WalletConnectSharp.Sign/Controllers/AddressProvider.cs b/WalletConnectSharp.Sign/Controllers/AddressProvider.cs new file mode 100644 index 0000000..0041f87 --- /dev/null +++ b/WalletConnectSharp.Sign/Controllers/AddressProvider.cs @@ -0,0 +1,185 @@ +using WalletConnectSharp.Sign.Interfaces; +using WalletConnectSharp.Sign.Models; +using WalletConnectSharp.Sign.Models.Engine.Events; + +namespace WalletConnectSharp.Sign.Controllers; + +public class AddressProvider : IAddressProvider +{ + public bool HasDefaultSession + { + get + { + return !string.IsNullOrWhiteSpace(DefaultSession.Topic) && DefaultSession.RequiredNamespaces != null; + } + } + + public string Name + { + get + { + return $"{_client.Name}-address-provider"; + } + } + + public string Context + { + get + { + return Name; + } + } + + public SessionStruct DefaultSession { get; set; } + public string DefaultNamespace { get; set; } + public string DefaultChain { get; set; } + public ISession Sessions { get; private set; } + + private ISignClient _client; + + public AddressProvider(ISignClient client) + { + this._client = client; + this.Sessions = client.Session; + + // set the first connected session to the default one + client.SessionConnected += ClientOnSessionConnected; + client.SessionDeleted += ClientOnSessionDeleted; + client.SessionUpdated += ClientOnSessionUpdated; + client.SessionApproved += ClientOnSessionConnected; + } + + private void ClientOnSessionUpdated(object sender, SessionEvent e) + { + if (DefaultSession.Topic == e.Topic) + { + UpdateDefaultChainAndNamespace(); + } + } + + private void ClientOnSessionDeleted(object sender, SessionEvent e) + { + if (DefaultSession.Topic == e.Topic) + { + DefaultSession = default; + UpdateDefaultChainAndNamespace(); + } + } + + private void ClientOnSessionConnected(object sender, SessionStruct e) + { + if (!HasDefaultSession) + { + DefaultSession = e; + UpdateDefaultChainAndNamespace(); + } + } + + private void UpdateDefaultChainAndNamespace() + { + if (HasDefaultSession) + { + var currentDefault = DefaultNamespace; + if (currentDefault != null && DefaultSession.RequiredNamespaces.ContainsKey(currentDefault)) + { + // DefaultNamespace is still valid + var currentChain = DefaultChain; + if (currentChain == null || + DefaultSession.RequiredNamespaces[DefaultNamespace].Chains.Contains(currentChain)) + { + // DefaultChain is still valid + return; + } + + DefaultChain = DefaultSession.RequiredNamespaces[DefaultNamespace].Chains[0]; + return; + } + + // DefaultNamespace is null or not found in RequiredNamespaces, update it + DefaultNamespace = DefaultSession.RequiredNamespaces.OrderedKeys.FirstOrDefault(); + if (DefaultNamespace != null) + { + DefaultChain = DefaultSession.RequiredNamespaces[DefaultNamespace].Chains[0]; + } + else + { + // TODO The Keys property is unordered! Maybe this needs to be updated + DefaultNamespace = DefaultSession.RequiredNamespaces.Keys.FirstOrDefault(); + if (DefaultNamespace != null) + { + DefaultChain = DefaultSession.RequiredNamespaces[DefaultNamespace].Chains[0]; + } + } + } + else + { + DefaultNamespace = null; + } + } + + public Caip25Address CurrentAddress(string @namespace = null, SessionStruct session = default) + { + @namespace ??= DefaultNamespace; + if (string.IsNullOrWhiteSpace(session.Topic)) // default + session = DefaultSession; + + // double check + if (@namespace == null) + throw new ArgumentException("CurrentAddress: @namespace is null"); + if (string.IsNullOrWhiteSpace(session.Topic)) + throw new ArgumentException("CurrentAddress: Session is undefined"); + + var defaultNamespace = session.Namespaces[@namespace]; + + if (defaultNamespace.Accounts.Length == 0) + return null; //The namespace {@namespace} has no addresses connected") + + var fullAddress = defaultNamespace.Accounts[0]; + var addressParts = fullAddress.Split(":"); + + var address = addressParts[2]; + var chainId = string.Join(':', addressParts.Take(2)); + + return new Caip25Address() + { + Address = address, + ChainId = chainId, + }; + } + + public Caip25Address[] AllAddresses(string @namespace = null, SessionStruct session = default) + { + @namespace ??= DefaultNamespace; + if (string.IsNullOrWhiteSpace(session.Topic)) // default + session = DefaultSession; + + // double check + if (@namespace == null) + throw new ArgumentException("CurrentAddress: @namespace is null"); + if (string.IsNullOrWhiteSpace(session.Topic)) + throw new ArgumentException("CurrentAddress: Session is undefined"); + + var defaultNamespace = session.Namespaces[@namespace]; + + if (defaultNamespace.Accounts.Length == 0) + return null; //The namespace {@namespace} has no addresses connected") + + return defaultNamespace.Accounts.Select(addr => new Caip25Address() + { + Address = addr.Split(":")[2], ChainId = string.Join(":", addr.Split(":").Take(2)) + }).ToArray(); + } + + public void Dispose() + { + _client.SessionConnected -= ClientOnSessionConnected; + _client.SessionDeleted -= ClientOnSessionDeleted; + _client.SessionUpdated -= ClientOnSessionUpdated; + _client.SessionApproved -= ClientOnSessionConnected; + + _client = null; + Sessions = null; + DefaultNamespace = null; + DefaultSession = default; + } +} diff --git a/WalletConnectSharp.Sign/Engine.cs b/WalletConnectSharp.Sign/Engine.cs index de57db1..8d48cf9 100644 --- a/WalletConnectSharp.Sign/Engine.cs +++ b/WalletConnectSharp.Sign/Engine.cs @@ -168,6 +168,43 @@ public void HandleEventMessageType(Func UpdateSession(Namespaces namespaces) + { + return UpdateSession(Client.AddressProvider.DefaultSession.Topic, namespaces); + } + + public Task Extend() + { + return Extend(Client.AddressProvider.DefaultSession.Topic); + } + + public Task Request(T data, string chainId = null, long? expiry = null) + { + return Request(Client.AddressProvider.DefaultSession.Topic, data, + chainId ?? Client.AddressProvider.DefaultChain, expiry); + } + + public Task Respond(JsonRpcResponse response) + { + return Respond(Client.AddressProvider.DefaultSession.Topic, response); + } + + public Task Emit(EventData eventData, string chainId = null) + { + return Emit(Client.AddressProvider.DefaultSession.Topic, eventData, + chainId ?? Client.AddressProvider.DefaultChain); + } + + public Task Ping() + { + return Ping(Client.AddressProvider.DefaultSession.Topic); + } + + public Task Disconnect(Error reason = null) + { + return Disconnect(Client.AddressProvider.DefaultSession.Topic, reason); + } + /// /// Parse a session proposal URI and return all information in the URI in a /// new object @@ -581,7 +618,7 @@ public async Task Request(string topic, T data, string chainId = null if (string.IsNullOrWhiteSpace(chainId)) { var sessionData = Client.Session.Get(topic); - var firstRequiredNamespace = sessionData.RequiredNamespaces.Keys.ToArray()[0]; + var firstRequiredNamespace = sessionData.RequiredNamespaces.OrderedKeys[0]; defaultChainId = sessionData.RequiredNamespaces[firstRequiredNamespace].Chains[0]; } else diff --git a/WalletConnectSharp.Sign/Interfaces/IAddressProvider.cs b/WalletConnectSharp.Sign/Interfaces/IAddressProvider.cs new file mode 100644 index 0000000..c5d107f --- /dev/null +++ b/WalletConnectSharp.Sign/Interfaces/IAddressProvider.cs @@ -0,0 +1,21 @@ +using WalletConnectSharp.Common; +using WalletConnectSharp.Sign.Models; + +namespace WalletConnectSharp.Sign.Interfaces; + +public interface IAddressProvider : IModule +{ + bool HasDefaultSession { get; } + + SessionStruct DefaultSession { get; set; } + + string DefaultNamespace { get; set; } + + string DefaultChain { get; set; } + + ISession Sessions { get; } + + Caip25Address CurrentAddress( string chain = null, SessionStruct session = default); + + Caip25Address[] AllAddresses(string chain = null, SessionStruct session = default); +} diff --git a/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs b/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs index b544125..ea6e918 100644 --- a/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs +++ b/WalletConnectSharp.Sign/Interfaces/IEngineAPI.cs @@ -198,5 +198,68 @@ public interface IEngineAPI void HandleEventMessageType(Func>, Task> requestCallback, Func, Task> responseCallback); + + /// + /// Update the default session, adding/removing additional namespaces in the given topic. The default session + /// is grabbed from Client.AddressProvider.DefaultSession + /// + /// The updated namespaces + /// A task that returns an interface that can be used to listen for acknowledgement of the updates + Task UpdateSession(Namespaces namespaces); + + /// + /// Extend the default session. + /// + /// A task that returns an interface that can be used to listen for acknowledgement of the extension + Task Extend(); + + /// + /// Send a request to the default session with the request data T. You may (optionally) specify + /// a chainId the request should be performed in. This function will await a response of type TR from the session. + /// + /// If no response is ever received, then a Timeout exception may be thrown. + /// + /// The type T MUST define the RpcMethodAttribute to tell the SDK what JSON RPC method to use for the given + /// type T. + /// Either type T or TR MUST define a RpcRequestOptions and RpcResponseOptions attribute to tell the SDK + /// what options to use for the Request / Response. + /// + /// The data of the request + /// An (optional) chainId the request should be performed in + /// 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 + /// The type of the request data. MUST define the RpcMethodAttribute + /// The type of the response data. + /// The response data as type TR + Task Request(T data, string chainId = null, long? expiry = null); + + /// + /// Send a response to a request to the default session with the response data TR. This function + /// can be called directly, however it may be easier to use event + /// to handle sending responses to specific requests. + /// + /// The JSON RPC response to send + /// The type of the request data + /// The type of the response data + Task Respond(JsonRpcResponse response); + + /// + /// Emit an event to the default session with the given . You may + /// optionally specify a chainId to specify where the event occured. + /// + /// The event data for the event emitted + /// An (optional) chainId to specify where the event occured + /// The type of the event data + Task Emit(EventData eventData, string chainId = null); + + /// + /// Send a ping to the default session + /// + Task Ping(); + + /// + /// Disconnect the default session with an (optional) error reason + /// + /// An (optional) error reason for the disconnect + Task Disconnect(Error reason = null); } } diff --git a/WalletConnectSharp.Sign/Interfaces/ISignClient.cs b/WalletConnectSharp.Sign/Interfaces/ISignClient.cs index 5367685..347fd00 100644 --- a/WalletConnectSharp.Sign/Interfaces/ISignClient.cs +++ b/WalletConnectSharp.Sign/Interfaces/ISignClient.cs @@ -16,6 +16,12 @@ public interface ISignClient : IModule, IEngineAPI /// Metadata Metadata { get; } + /// + /// The module that holds the logic for handling the default session & chain, and for fetching the current address + /// for any session or the default session. + /// + IAddressProvider AddressProvider { get; } + /// /// The module this Sign Client is using /// diff --git a/WalletConnectSharp.Sign/Internals/EngineHandler.cs b/WalletConnectSharp.Sign/Internals/EngineHandler.cs index f06fee2..75bb29b 100644 --- a/WalletConnectSharp.Sign/Internals/EngineHandler.cs +++ b/WalletConnectSharp.Sign/Internals/EngineHandler.cs @@ -156,7 +156,8 @@ async Task IEnginePrivate.OnSessionSettleRequest(string topic, JsonRpcRequest p.PairingTopic == pairingTopic).RequiredNamespaces, }; await MessageHandler.SendResult(payload.Id, topic, true); this.SessionConnected?.Invoke(this, session); diff --git a/WalletConnectSharp.Sign/Internals/EngineValidation.cs b/WalletConnectSharp.Sign/Internals/EngineValidation.cs index 0a19a81..5359925 100644 --- a/WalletConnectSharp.Sign/Internals/EngineValidation.cs +++ b/WalletConnectSharp.Sign/Internals/EngineValidation.cs @@ -115,7 +115,7 @@ Task IEnginePrivate.IsValidSessionSettleRequest(SessionSettle settle) var controller = settle.Controller; var namespaces = settle.Namespaces; var expiry = settle.Expiry; - if (relay.Protocol != null && string.IsNullOrWhiteSpace(relay.Protocol)) + if (relay != null && string.IsNullOrWhiteSpace(relay.Protocol)) { throw WalletConnectException.FromType(ErrorType.MISSING_OR_INVALID, $"OnSessionSettleRequest() relay protocol should be a string"); diff --git a/WalletConnectSharp.Sign/Models/Caip25Address.cs b/WalletConnectSharp.Sign/Models/Caip25Address.cs new file mode 100644 index 0000000..a237842 --- /dev/null +++ b/WalletConnectSharp.Sign/Models/Caip25Address.cs @@ -0,0 +1,7 @@ +namespace WalletConnectSharp.Sign.Models; + +public class Caip25Address +{ + public string Address; + public string ChainId; +} diff --git a/WalletConnectSharp.Sign/Models/RequiredNamespaces.cs b/WalletConnectSharp.Sign/Models/RequiredNamespaces.cs index d093d17..bbd78d4 100644 --- a/WalletConnectSharp.Sign/Models/RequiredNamespaces.cs +++ b/WalletConnectSharp.Sign/Models/RequiredNamespaces.cs @@ -12,6 +12,22 @@ namespace WalletConnectSharp.Sign.Models /// public class RequiredNamespaces : Dictionary, IEquatable { + private List _orderedKeys = new(); + + public List OrderedKeys => _orderedKeys; + + public new void Add(string key, ProposedNamespace value) + { + base.Add(key, value); + _orderedKeys.Add(key); + } + + public new void Remove(string key) + { + base.Remove(key); + _orderedKeys.Remove(key); + } + public bool Equals(RequiredNamespaces other) { return new DictionaryComparer(ProposedNamespace.RequiredNamespaceComparer).Equals(this, other); diff --git a/WalletConnectSharp.Sign/WalletConnectSignClient.cs b/WalletConnectSharp.Sign/WalletConnectSignClient.cs index 9a57fc8..5645430 100644 --- a/WalletConnectSharp.Sign/WalletConnectSignClient.cs +++ b/WalletConnectSharp.Sign/WalletConnectSignClient.cs @@ -57,7 +57,9 @@ public class WalletConnectSignClient : ISignClient /// The Metadata for this instance of the Sign Client module /// public Metadata Metadata { get; } - + + public IAddressProvider AddressProvider { get; } + /// /// The module this Sign Client module is using /// @@ -201,6 +203,7 @@ private WalletConnectSignClient(SignClientOptions options) Session = new Session(Core); Proposal = new Proposal(Core); Engine = new Engine(this); + AddressProvider = new AddressProvider(this); SetupEvents(); } @@ -434,6 +437,41 @@ public void HandleEventMessageType(Func(requestCallback, responseCallback); } + public Task UpdateSession(Namespaces namespaces) + { + return this.Engine.UpdateSession(namespaces); + } + + public Task Extend() + { + return this.Engine.Extend(); + } + + public Task Request(T data, string chainId = null, long? expiry = null) + { + return this.Engine.Request(data, chainId, expiry); + } + + public Task Respond(JsonRpcResponse response) + { + return this.Engine.Respond(response); + } + + public Task Emit(EventData eventData, string chainId = null) + { + return this.Engine.Emit(eventData, chainId); + } + + public Task Ping() + { + return this.Engine.Ping(); + } + + public Task Disconnect(Error reason = null) + { + return this.Engine.Disconnect(reason); + } + private async Task Initialize() { await this.Core.Start(); From 625d757442a17c94096da4a131dc9aba7abfb9d4 Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Sun, 5 Nov 2023 23:04:31 -0500 Subject: [PATCH 2/3] feat: move address fetching logic to SessionStruct --- .../Controllers/AddressProvider.cs | 39 +--------------- .../Models/SessionStruct.cs | 45 +++++++++++++++++++ 2 files changed, 47 insertions(+), 37 deletions(-) diff --git a/WalletConnectSharp.Sign/Controllers/AddressProvider.cs b/WalletConnectSharp.Sign/Controllers/AddressProvider.cs index 0041f87..47b7383 100644 --- a/WalletConnectSharp.Sign/Controllers/AddressProvider.cs +++ b/WalletConnectSharp.Sign/Controllers/AddressProvider.cs @@ -123,28 +123,7 @@ public Caip25Address CurrentAddress(string @namespace = null, SessionStruct sess if (string.IsNullOrWhiteSpace(session.Topic)) // default session = DefaultSession; - // double check - if (@namespace == null) - throw new ArgumentException("CurrentAddress: @namespace is null"); - if (string.IsNullOrWhiteSpace(session.Topic)) - throw new ArgumentException("CurrentAddress: Session is undefined"); - - var defaultNamespace = session.Namespaces[@namespace]; - - if (defaultNamespace.Accounts.Length == 0) - return null; //The namespace {@namespace} has no addresses connected") - - var fullAddress = defaultNamespace.Accounts[0]; - var addressParts = fullAddress.Split(":"); - - var address = addressParts[2]; - var chainId = string.Join(':', addressParts.Take(2)); - - return new Caip25Address() - { - Address = address, - ChainId = chainId, - }; + return session.CurrentAddress(@namespace); } public Caip25Address[] AllAddresses(string @namespace = null, SessionStruct session = default) @@ -153,21 +132,7 @@ public Caip25Address[] AllAddresses(string @namespace = null, SessionStruct sess if (string.IsNullOrWhiteSpace(session.Topic)) // default session = DefaultSession; - // double check - if (@namespace == null) - throw new ArgumentException("CurrentAddress: @namespace is null"); - if (string.IsNullOrWhiteSpace(session.Topic)) - throw new ArgumentException("CurrentAddress: Session is undefined"); - - var defaultNamespace = session.Namespaces[@namespace]; - - if (defaultNamespace.Accounts.Length == 0) - return null; //The namespace {@namespace} has no addresses connected") - - return defaultNamespace.Accounts.Select(addr => new Caip25Address() - { - Address = addr.Split(":")[2], ChainId = string.Join(":", addr.Split(":").Take(2)) - }).ToArray(); + return session.AllAddresses(@namespace); } public void Dispose() diff --git a/WalletConnectSharp.Sign/Models/SessionStruct.cs b/WalletConnectSharp.Sign/Models/SessionStruct.cs index f34872b..bc2071c 100644 --- a/WalletConnectSharp.Sign/Models/SessionStruct.cs +++ b/WalletConnectSharp.Sign/Models/SessionStruct.cs @@ -82,5 +82,50 @@ public string Key return Topic; } } + + public Caip25Address CurrentAddress(string @namespace) + { + // double check + if (@namespace == null) + throw new ArgumentException("SessionStruct.CurrentAddress: @namespace is null"); + if (string.IsNullOrWhiteSpace(Topic)) + throw new ArgumentException("SessionStruct.CurrentAddress: Session is undefined"); + + var defaultNamespace = Namespaces[@namespace]; + + if (defaultNamespace.Accounts.Length == 0) + return null; //The namespace {@namespace} has no addresses connected") + + var fullAddress = defaultNamespace.Accounts[0]; + var addressParts = fullAddress.Split(":"); + + var address = addressParts[2]; + var chainId = string.Join(':', addressParts.Take(2)); + + return new Caip25Address() + { + Address = address, + ChainId = chainId, + }; + } + + public Caip25Address[] AllAddresses(string @namespace) + { + // double check + if (@namespace == null) + throw new ArgumentException("SessionStruct.AllAddresses: @namespace is null"); + if (string.IsNullOrWhiteSpace(Topic)) + throw new ArgumentException("SessionStruct.AllAddresses: Session is undefined"); + + var defaultNamespace = Namespaces[@namespace]; + + if (defaultNamespace.Accounts.Length == 0) + return null; //The namespace {@namespace} has no addresses connected") + + return defaultNamespace.Accounts.Select(addr => new Caip25Address() + { + Address = addr.Split(":")[2], ChainId = string.Join(":", addr.Split(":").Take(2)) + }).ToArray(); + } } } From 505a32e658143f293b0f889c11c771d29ffe749e Mon Sep 17 00:00:00 2001 From: Julia Seward Date: Sun, 5 Nov 2023 23:05:39 -0500 Subject: [PATCH 3/3] chore: convert caip25address to a struct --- WalletConnectSharp.Sign/Models/Caip25Address.cs | 2 +- WalletConnectSharp.Sign/Models/SessionStruct.cs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/WalletConnectSharp.Sign/Models/Caip25Address.cs b/WalletConnectSharp.Sign/Models/Caip25Address.cs index a237842..25adcb1 100644 --- a/WalletConnectSharp.Sign/Models/Caip25Address.cs +++ b/WalletConnectSharp.Sign/Models/Caip25Address.cs @@ -1,6 +1,6 @@ namespace WalletConnectSharp.Sign.Models; -public class Caip25Address +public struct Caip25Address { public string Address; public string ChainId; diff --git a/WalletConnectSharp.Sign/Models/SessionStruct.cs b/WalletConnectSharp.Sign/Models/SessionStruct.cs index bc2071c..48035f4 100644 --- a/WalletConnectSharp.Sign/Models/SessionStruct.cs +++ b/WalletConnectSharp.Sign/Models/SessionStruct.cs @@ -92,9 +92,10 @@ public Caip25Address CurrentAddress(string @namespace) throw new ArgumentException("SessionStruct.CurrentAddress: Session is undefined"); var defaultNamespace = Namespaces[@namespace]; - + if (defaultNamespace.Accounts.Length == 0) - return null; //The namespace {@namespace} has no addresses connected") + throw new Exception( + $"SessionStruct.CurrentAddress: Given namespace {@namespace} has no connected addresses"); var fullAddress = defaultNamespace.Accounts[0]; var addressParts = fullAddress.Split(":");