diff --git a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/ChainSafe.Gaming.Unity.ThirdParty.dll b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/ChainSafe.Gaming.Unity.ThirdParty.dll index 508802947..11e619705 100644 Binary files a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/ChainSafe.Gaming.Unity.ThirdParty.dll and b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/ChainSafe.Gaming.Unity.ThirdParty.dll differ diff --git a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/ChainSafe.Gaming.Unity.dll b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/ChainSafe.Gaming.Unity.dll index eeba98149..cabe8f62f 100644 Binary files a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/ChainSafe.Gaming.Unity.dll and b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/ChainSafe.Gaming.Unity.dll differ diff --git a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/ChainSafe.Gaming.WalletConnect.dll b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/ChainSafe.Gaming.WalletConnect.dll index 747a354e2..819083b45 100644 Binary files a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/ChainSafe.Gaming.WalletConnect.dll and b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/ChainSafe.Gaming.WalletConnect.dll differ diff --git a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Auth.dll b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Auth.dll index c768c4f82..04eda99bd 100755 Binary files a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Auth.dll and b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Auth.dll differ diff --git a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Common.dll b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Common.dll index 694c02890..d3f51a3a7 100755 Binary files a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Common.dll and b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Common.dll differ diff --git a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Core.dll b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Core.dll index 63c806d1e..699ca3237 100755 Binary files a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Core.dll and b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Core.dll differ diff --git a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Crypto.dll b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Crypto.dll index 2f8a939f8..addac005d 100755 Binary files a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Crypto.dll and b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Crypto.dll differ diff --git a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Events.dll b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Events.dll index 5eb3f817b..eaf88574f 100755 Binary files a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Events.dll and b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Events.dll differ diff --git a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Network.Websocket.dll b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Network.Websocket.dll index f7c4f1113..84e6cea21 100755 Binary files a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Network.Websocket.dll and b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Network.Websocket.dll differ diff --git a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Network.dll b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Network.dll index fb7c96663..24c0e00c6 100755 Binary files a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Network.dll and b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Network.dll differ diff --git a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Sign.dll b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Sign.dll index 0be79a3a0..8f2689a24 100755 Binary files a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Sign.dll and b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Sign.dll differ diff --git a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Storage.dll b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Storage.dll index c3f2fd437..a628b38f8 100755 Binary files a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Storage.dll and b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Storage.dll differ diff --git a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Web3Wallet.dll b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Web3Wallet.dll index ba0d134b9..ea9a3bdef 100755 Binary files a/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Web3Wallet.dll and b/Packages/io.chainsafe.web3-unity/Runtime/Libraries/WalletConnectSharp.Web3Wallet.dll differ diff --git a/Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/WalletConnectWebSocketBuilder.cs b/Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/NativeWebSocketConnectionBuilder.cs similarity index 59% rename from Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/WalletConnectWebSocketBuilder.cs rename to Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/NativeWebSocketConnectionBuilder.cs index e0b1f1f41..1a44bf667 100644 --- a/Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/WalletConnectWebSocketBuilder.cs +++ b/Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/NativeWebSocketConnectionBuilder.cs @@ -12,7 +12,7 @@ namespace ChainSafe.Gaming.WalletConnect /// We need this because of Unity's IL2CPP build code stripping and reachability issue. /// For version 2022 and above this issue has been fixed by Unity as stated here https://blog.unity.com/engine-platform/il2cpp-full-generic-sharing-in-unity-2022-1-beta so this custom implementation isn't needed. /// - public class WalletConnectWebSocketBuilder : MonoBehaviour, IConnectionBuilder + public class NativeWebSocketConnectionBuilder : MonoBehaviour, IConnectionBuilder { /// /// Create WebSocket connection for Wallet Connect. @@ -21,20 +21,7 @@ public class WalletConnectWebSocketBuilder : MonoBehaviour, IConnectionBuilder /// Created connection. public Task CreateConnection(string url) { - TaskCompletionSource taskCompletionSource = - new TaskCompletionSource(); - - Dispatcher.Instance().Enqueue(() => - { - Debug.Log("Building websocket with URL " + url); - var websocket = gameObject.AddComponent(); - websocket.Url = url; - - taskCompletionSource.TrySetResult(websocket); - }); - - - return taskCompletionSource.Task; + return Task.FromResult(new WebSocketConnection(url)); } } } diff --git a/Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/WalletConnectWebSocketBuilder.cs.meta b/Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/NativeWebSocketConnectionBuilder.cs.meta similarity index 100% rename from Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/WalletConnectWebSocketBuilder.cs.meta rename to Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/NativeWebSocketConnectionBuilder.cs.meta diff --git a/Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/WalletConnectWebSocket.cs b/Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/WalletConnectWebSocket.cs deleted file mode 100644 index c29c83657..000000000 --- a/Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/WalletConnectWebSocket.cs +++ /dev/null @@ -1,357 +0,0 @@ -#if !UNITY_2022_1_OR_NEWER -using System; -using System.IO; -using System.Text; -using System.Threading.Tasks; -using NativeWebSocket; -using Newtonsoft.Json; -using UnityEngine; -using WalletConnectSharp.Common; -using WalletConnectSharp.Common.Logging; -using WalletConnectSharp.Common.Utils; -using WalletConnectSharp.Events; -using WalletConnectSharp.Events.Model; -using WalletConnectSharp.Network; -using WalletConnectSharp.Network.Models; -using WalletConnectSharp.Network.Websocket; - -namespace ChainSafe.Gaming.WalletConnect -{ - /// - /// Custom web socket implementation for Wallet Connect, based on NativeWebSocket https://github.com/endel/NativeWebSocket. - /// - public class WalletConnectWebSocket : MonoBehaviour, IJsonRpcConnection, IModule - { - private const string AddressNotFoundError = "getaddrinfo ENOTFOUND"; - private const string ConnectionRefusedError = "connect ECONNREFUSED"; - - private WebSocket webSocket; - - private EventDelegator delegator; - - private string url; - - private bool registering; - - private Guid contextId; - - private TaskCompletionSource beginConnection; - - private bool connectionStarted; - - private TimeSpan connectionTimeout = TimeSpan.FromSeconds(60); - - /// - /// The Url to connect to - /// - public string Url - { - get => url; - set => url = value; - } - - public bool IsPaused { get; private set; } - - /// - /// The name of this websocket connection module - /// - public string Name => "websocket-connection"; - - /// - /// The context string of this Websocket module - /// - public string Context => contextId.ToString(); - - /// - /// The EventDelegator this Websocket connection module is using - /// - public EventDelegator Events => delegator; - - /// - /// Whether this websocket connection is connected - /// - public bool Connected - { - get - { - bool connected = webSocket != null && webSocket.State == WebSocketState.Open; - WCLogger.Log("Websocket Connected State: " + connected); - return connected; - } - } - - /// - /// Whether this websocket connection is currently connecting - /// - public bool Connecting => registering; - - public async void Dispose() - { - if (Connected) - { - WCLogger.Log("Socket is being disposed, cleanup"); - await Close(); - } - } - - /// - /// Open Socket. - /// - public async Task Open() - { - await Register(this.url); - } - - /// - /// Open Socket. - /// - /// Socket options. - /// Options type. - /// - public Task Open(T options) - { - if (typeof(string).IsAssignableFrom(typeof(T))) - { - return Register(options as string); - } - - return Open(); - } - - /// - /// Close socket. - /// - /// Throws if socket is already closed. - public async Task Close() - { - if (webSocket == null) throw new IOException("Connection already closed"); - - await webSocket.Close(); - - WCLogger.Log("Closing websocket due to Close() being called"); - OnClose(WebSocketCloseCode.Normal); - } - - /// - /// Send request using socket. - /// - /// Payload to be sent. - /// Socket context. - /// Payload Type. - public async Task SendRequest(IJsonRpcRequest requestPayload, object context) - { - if (webSocket == null) webSocket = await Register(this.url); - - try - { - Debug.Log($"[WCWebSocket-{contextId}] Sending request {JsonConvert.SerializeObject(requestPayload)}"); - - await webSocket.SendText(JsonConvert.SerializeObject(requestPayload)); - } - catch (Exception e) - { - Debug.LogError(e); - - OnError(requestPayload, e); - } - } - - /// - /// Send result using socket. - /// - /// Payload to be sent. - /// Socket context. - /// Payload Type. - public async Task SendResult(IJsonRpcResult responsePayload, object context) - { - if (webSocket == null) webSocket = await Register(this.url); - - try - { - await webSocket.SendText(JsonConvert.SerializeObject(responsePayload)); - } - catch (Exception e) - { - OnError(responsePayload, e); - } - } - - /// - /// Send error using socket. - /// - /// Payload to be sent. - /// Socket context. - public async Task SendError(IJsonRpcError errorPayload, object context) - { - if (webSocket == null) webSocket = await Register(this.url); - - try - { - await webSocket.SendText(JsonConvert.SerializeObject(errorPayload)); - } - catch (Exception e) - { - OnError(errorPayload, e); - } - } - - private void Awake() - { - contextId = Guid.NewGuid(); - delegator = new EventDelegator(this); - } - - private async Task Register(string newUrl) - { - if (!Validation.IsWsUrl(newUrl)) - { - throw new ArgumentException("Provided URL is not compatible with WebSocket connection: " + newUrl); - } - - if (registering) - { - TaskCompletionSource registeringTask = - new TaskCompletionSource(TaskCreationOptions.None); - - Events.ListenForOnce(WebsocketConnectionEvents.RegisterError, - delegate(object sender, GenericEvent @event) - { - registeringTask.SetException(@event.EventData); - }); - - Events.ListenForOnce(WebsocketConnectionEvents.Open, - delegate(object sender, GenericEvent @event) - { - registeringTask.SetResult(@event.EventData); - }); - - await registeringTask.Task; - - return registeringTask.Task.Result; - } - - this.url = newUrl; - this.registering = true; - - try - { - webSocket = new WebSocket(this.url); - //_socket = new WebsocketClient(new Uri(_url)); - - await StartWebsocket(webSocket).WithTimeout(connectionTimeout, "Unavailable WS RPC url at " + this.url); - OnOpen(webSocket); - return webSocket; - } - catch (Exception e) - { - Events.Trigger(WebsocketConnectionEvents.RegisterError, e); - - WCLogger.Log($"Calling close due to exception {e}"); - OnClose(WebSocketCloseCode.ServerError); - - throw; - } - } - - private Task StartWebsocket(WebSocket socket) - { - this.webSocket = socket; - connectionStarted = false; - beginConnection = new TaskCompletionSource(); - - return beginConnection.Task; - } - - private void OnOpen(WebSocket socket) - { - socket.OnMessage += OnPayload; - socket.OnClose += OnDisconnect; - - this.registering = false; - Events.Trigger(WebsocketConnectionEvents.Open, this.webSocket); - } - - private void OnDisconnect(WebSocketCloseCode code) - { - if (code != WebSocketCloseCode.Normal) Events.Trigger(WebsocketConnectionEvents.Error, code); - - WCLogger.Log("Socket closing due to Disconnect event from socket"); - OnClose(code); - } - - private void OnClose(WebSocketCloseCode code) - { - if (this.webSocket == null) return; - - //_socket.Dispose(); - this.webSocket = null; - this.registering = false; - Events.Trigger(WebsocketConnectionEvents.Close, code); - } - - private void OnPayload(byte[] data) - { - string json = Encoding.UTF8.GetString(data); - - Debug.Log($"[WCWebSocket-{contextId}] Got payload {json}"); - - if (string.IsNullOrWhiteSpace(json)) return; - - Debug.Log($"[WCWebsocket-{contextId}] Triggering payload event with JSON {json}"); - - Events.Trigger(WebsocketConnectionEvents.Payload, json); - } - - void Update() - { -#if !UNITY_WEBGL || UNITY_EDITOR - if (webSocket != null) webSocket.DispatchMessageQueue(); -#endif - } - - private async void LateUpdate() - { - if (connectionStarted || beginConnection == null || beginConnection.Task.IsCompleted) return; - if (webSocket == null) return; - - connectionStarted = true; - this.webSocket.OnOpen += SocketOnOnOpen; - try - { - await this.webSocket.Connect(); - } - catch (Exception e) - { - this.beginConnection.TrySetException(e); - } - } - - private void SocketOnOnOpen() - { - beginConnection.TrySetResult(true); - } - - private void OnError(IJsonRpcPayload payload, Exception e) - { - var exception = e.Message.Contains(AddressNotFoundError) || e.Message.Contains(ConnectionRefusedError) - ? new IOException("Unavailable WS RPC url at " + this.url) - : e; - - string message = exception.Message; - - var response = new JsonRpcResponse(payload.Id, - new Error() { Code = exception.HResult, Data = null, Message = message }, default(T)); - - //Trigger the payload event, converting the new JsonRpcResponse object to JSON string - Events.Trigger(WebsocketConnectionEvents.Payload, JsonConvert.SerializeObject(response)); - - Debug.LogError(e); - } - - private void OnApplicationPause(bool isPaused) - { - IsPaused = isPaused; - } - } -} -#endif \ No newline at end of file diff --git a/Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/WebSocketConnection.cs b/Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/WebSocketConnection.cs new file mode 100644 index 000000000..1b2803c48 --- /dev/null +++ b/Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/WebSocketConnection.cs @@ -0,0 +1,280 @@ +#if !UNITY_2022_1_OR_NEWER +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using ChainSafe.Gaming.Evm.Unity; +using NativeWebSocket; +using Newtonsoft.Json; +using UnityEngine; +using WalletConnectSharp.Common; +using WalletConnectSharp.Common.Logging; +using WalletConnectSharp.Common.Utils; +using WalletConnectSharp.Events; +using WalletConnectSharp.Events.Model; +using WalletConnectSharp.Network; +using WalletConnectSharp.Network.Models; +using WalletConnectSharp.Network.Websocket; + +namespace ChainSafe.Gaming.WalletConnect +{ + /// + /// Custom web socket implementation for Wallet Connect, based on NativeWebSocket https://github.com/endel/NativeWebSocket. + /// + public class WebSocketConnection : IJsonRpcConnection, IModule + { + public string Name => "websocket-connection"; + + public string Context { get; } + + public bool Connected { get; set; } + + public bool Connecting { get; private set; } + + public string Url { get; private set; } + + public bool IsPaused { get; private set; } + + public event EventHandler PayloadReceived; + public event EventHandler Closed; + public event EventHandler ErrorReceived; + public event EventHandler Opened; + public event EventHandler RegisterErrored; + + private WebSocket _socket; + private bool _disposed; + + public WebSocketConnection(string url) + { + if (!Validation.IsWsUrl(url)) + throw new ArgumentException( + $"[WebSocketConnection] Provided URL is not compatible with WebSocket connection: {url}"); + + Context = Guid.NewGuid().ToString(); + Url = url; + } + + Task IJsonRpcConnection.Open() + { + Register(); + return Task.CompletedTask; + } + + Task IJsonRpcConnection.Open(T options) + { + if (_disposed) + throw new ObjectDisposedException(nameof(WebSocketConnection)); + + if (typeof(string).IsAssignableFrom(typeof(T))) + { + var newUrl = options as string; + + if (!Validation.IsWsUrl(newUrl)) + throw new ArgumentException( + $"[WebSocketConnection] Provided URL is not compatible with WebSocket connection: {newUrl}"); + + Url = newUrl; + } + + Register(); + return Task.CompletedTask; + } + + async Task IJsonRpcConnection.Close() + { + if (!Connected || _socket == null) + throw new IOException("Connection already closed"); + + if (_disposed) + throw new ObjectDisposedException(nameof(WebSocketConnection)); + + WCLogger.Log("Closing websocket due to Close() being called"); + + await _socket.Close(); + OnClose(WebSocketCloseCode.Normal); + } + + async Task IJsonRpcConnection.SendRequest(IJsonRpcRequest requestPayload, object context) + { + if (_socket == null) + throw new IOException("Connection is not open"); + + if (_disposed) + throw new ObjectDisposedException(nameof(WebSocketConnection)); + + try + { + var json = JsonConvert.SerializeObject(requestPayload); + WCLogger.Log($"[WebSocketConnection-{Context}] Sending request {json}"); + + await _socket.SendText(json); + + PayloadReceived?.Invoke(this, json); + } + catch (Exception e) + { + OnError(requestPayload, e); + } + } + + async Task IJsonRpcConnection.SendResult(IJsonRpcResult responsePayload, object context) + { + if (_socket == null) + throw new IOException("Connection is not open"); + + if (_disposed) + throw new ObjectDisposedException(nameof(WebSocketConnection)); + + try + { + await _socket.SendText(JsonConvert.SerializeObject(responsePayload)); + } + catch (Exception e) + { + OnError(responsePayload, e); + } + } + + async Task IJsonRpcConnection.SendError(IJsonRpcError errorPayload, object context) + { + if (_socket == null) + throw new IOException("Connection is not open"); + + try + { + await _socket.SendText(JsonConvert.SerializeObject(errorPayload)); + } + catch (Exception e) + { + OnError(errorPayload, e); + } + } + + private void Register() + { + if (!Validation.IsWsUrl(Url)) + throw new ArgumentException($"Provided URL is not compatible with WebSocket connection: {Url}"); + + if (Connecting || Connected) + throw new InvalidOperationException("WebSocket is already connecting or connected"); + + Connecting = true; + + _socket = new WebSocket(Url); + _socket.OnOpen += OnOpen; + _socket.OnMessage += OnMessage; + _socket.OnClose += OnDisconnect; + _socket.OnError += OnError; + +#if !UNITY_WEBGL || UNITY_EDITOR + Dispatcher.Instance().OnTick += OnTick; +#endif + Dispatcher.Instance().OnApplicationPaused += OnApplicationPause; + + try + { + Connect(); + } + catch (Exception e) + { + OnError(null, e); + throw; + } + } + +#if !UNITY_WEBGL || UNITY_EDITOR + private void OnTick() + { + _socket.DispatchMessageQueue(); + } +#endif + + private void OnApplicationPause(bool status) + { + IsPaused = status; + } + + private void OnOpen() + { + Connected = true; + Connecting = false; + + Opened?.Invoke(this, _socket); + } + + private void OnMessage(byte[] data) + { + var json = System.Text.Encoding.UTF8.GetString(data); + + WCLogger.Log($"[WebSocketConnection-{Context}] Got payload: \n{json}"); + + if (string.IsNullOrWhiteSpace(json)) + return; + + PayloadReceived?.Invoke(this, json); + } + + private void OnDisconnect(WebSocketCloseCode obj) + { + if (obj != WebSocketCloseCode.Normal) + ErrorReceived?.Invoke(this, new Exception($"Websocket closed with code {obj}")); + + OnClose(obj); + } + + private void OnError(string message) + { + ErrorReceived?.Invoke(this, new Exception(message)); + WCLogger.LogError(Connecting + ? $"[{Name}-{Context}] Error happened during connection. Make sure Project ID is valid. Error message: {message}" + : $"[{Name}-{Context}] Error: {message}"); + } + + private void OnError(IJsonRpcPayload ogPayload, Exception e) + { + if (ogPayload != null) + { + var payload = new JsonRpcResponse(ogPayload.Id, new Error() + { + Code = e.HResult, + Data = null, + Message = e.Message + }, default); + + var json = JsonConvert.SerializeObject(payload); + + //Trigger the payload event, converting the new JsonRpcResponse object to JSON string + PayloadReceived?.Invoke(this, json); + } + + WCLogger.LogError(e); + } + + private void OnClose(WebSocketCloseCode obj) + { + if (_socket == null) + return; + +#if !UNITY_WEBGL || UNITY_EDITOR + Dispatcher.Instance().OnTick -= OnTick; +#endif + Dispatcher.Instance().OnApplicationPaused -= OnApplicationPause; + + _socket = null; + Connected = false; + Closed?.Invoke(this, EventArgs.Empty); + } + + // This is a workaround for NativeWebSocket.Connect() blocking the main thread + private async void Connect() + { + await _socket.Connect(); + } + + public async void Dispose() + { + await _socket.Close(); + } + } +} +#endif \ No newline at end of file diff --git a/Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/WalletConnectWebSocket.cs.meta b/Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/WebSocketConnection.cs.meta similarity index 100% rename from Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/WalletConnectWebSocket.cs.meta rename to Packages/io.chainsafe.web3-unity/Runtime/Scripts/WalletConnect/WebSocketConnection.cs.meta diff --git a/Packages/io.chainsafe.web3-unity/Samples~/Web3.Unity/Scripts/Scenes/ExistingWalletLogin.cs b/Packages/io.chainsafe.web3-unity/Samples~/Web3.Unity/Scripts/Scenes/ExistingWalletLogin.cs index 08dcf0b0c..7b2878b26 100644 --- a/Packages/io.chainsafe.web3-unity/Samples~/Web3.Unity/Scripts/Scenes/ExistingWalletLogin.cs +++ b/Packages/io.chainsafe.web3-unity/Samples~/Web3.Unity/Scripts/Scenes/ExistingWalletLogin.cs @@ -39,7 +39,7 @@ public class ExistingWalletLogin : Login #if !UNITY_2022_1_OR_NEWER // Use a custom connection builder due to an issue fixed in version 2022 and above https://blog.unity.com/engine-platform/il2cpp-full-generic-sharing-in-unity-2022-1-beta. - private WalletConnectWebSocketBuilder connectionBuilder; + private NativeWebSocketConnectionBuilder connectionBuilder; #endif [Header("Wallet Connect")] [SerializeField] @@ -82,15 +82,15 @@ protected override IEnumerator Initialize() #if !UNITY_2022_1_OR_NEWER - connectionBuilder = FindObjectOfType(); + connectionBuilder = FindObjectOfType(); // Initialize custom web socket if it's not already. if (connectionBuilder == null) { GameObject webSocketBuilderObj = - new GameObject(nameof(WalletConnectWebSocketBuilder), typeof(WalletConnectWebSocketBuilder)); + new GameObject(nameof(NativeWebSocketConnectionBuilder), typeof(NativeWebSocketConnectionBuilder)); - connectionBuilder = webSocketBuilderObj.GetComponent(); + connectionBuilder = webSocketBuilderObj.GetComponent(); // keep web socket during scene unload DontDestroyOnLoad(webSocketBuilderObj); diff --git a/Packages/io.chainsafe.web3-unity/Tests/Runtime/EvmTests.cs b/Packages/io.chainsafe.web3-unity/Tests/Runtime/EvmTests.cs index 8764b25f0..ef3359641 100644 --- a/Packages/io.chainsafe.web3-unity/Tests/Runtime/EvmTests.cs +++ b/Packages/io.chainsafe.web3-unity/Tests/Runtime/EvmTests.cs @@ -322,7 +322,7 @@ public IEnumerator TestTransferErc20() [UnityTest] public IEnumerator TestTransferErc721() { - config.TestResponse = "0x5c02df6b55bbc5f958d3a5e4b21f0db20cd2284fabe1f7278f7984d3e05de81d"; + config.TestResponse = "0xba034c4150f2a5fd50926551a8e95028d51dcc91e3c3b566bbd316968bc29375"; var transferErc721 = Erc721.TransferErc721(web3, Contracts.Erc721, SendToAddress, Transfer721Id); yield return new WaitUntil(() => transferErc721.IsCompleted); if (transferErc721.Exception != null) throw transferErc721.Exception; diff --git a/src/ChainSafe.Gaming.Unity.ThirdParty/Dispatcher.cs b/src/ChainSafe.Gaming.Unity.ThirdParty/Dispatcher.cs index 8c9ba9de1..58d81b9ad 100644 --- a/src/ChainSafe.Gaming.Unity.ThirdParty/Dispatcher.cs +++ b/src/ChainSafe.Gaming.Unity.ThirdParty/Dispatcher.cs @@ -29,17 +29,23 @@ namespace ChainSafe.Gaming.Evm.Unity /// public class Dispatcher : MonoBehaviour { - private static readonly Queue _executionQueue = new Queue(); + private static readonly Queue ExecutionQueue = new Queue(); + + public event Action OnApplicationPaused; + + public event Action OnTick; public void Update() { - lock (_executionQueue) + lock (ExecutionQueue) { - while (_executionQueue.Count > 0) + while (ExecutionQueue.Count > 0) { - _executionQueue.Dequeue().Invoke(); + ExecutionQueue.Dequeue().Invoke(); } } + + OnTick?.Invoke(); } /// @@ -48,9 +54,9 @@ public void Update() /// IEnumerator function that will be executed from the main thread. public void Enqueue(IEnumerator action) { - lock (_executionQueue) + lock (ExecutionQueue) { - _executionQueue.Enqueue(() => { StartCoroutine(action); }); + ExecutionQueue.Enqueue(() => { StartCoroutine(action); }); } } @@ -176,6 +182,11 @@ public static Dispatcher Initialize() return _instance; } + private void OnApplicationPause(bool pauseStatus) + { + OnApplicationPaused?.Invoke(pauseStatus); + } + void OnDestroy() { _instance = null; diff --git a/src/ChainSafe.Gaming.WalletConnect/ChainSafe.Gaming.WalletConnect.csproj b/src/ChainSafe.Gaming.WalletConnect/ChainSafe.Gaming.WalletConnect.csproj index 5069c78e2..beafd2164 100644 --- a/src/ChainSafe.Gaming.WalletConnect/ChainSafe.Gaming.WalletConnect.csproj +++ b/src/ChainSafe.Gaming.WalletConnect/ChainSafe.Gaming.WalletConnect.csproj @@ -17,7 +17,8 @@ - + + diff --git a/src/ChainSafe.Gaming.WalletConnect/WalletConnectCustomProvider.cs b/src/ChainSafe.Gaming.WalletConnect/WalletConnectCustomProvider.cs index 37071674e..19e934c50 100644 --- a/src/ChainSafe.Gaming.WalletConnect/WalletConnectCustomProvider.cs +++ b/src/ChainSafe.Gaming.WalletConnect/WalletConnectCustomProvider.cs @@ -12,7 +12,7 @@ using WalletConnectSharp.Common.Utils; using WalletConnectSharp.Core; using WalletConnectSharp.Core.Models; -using WalletConnectSharp.Core.Models.Relay; +using WalletConnectSharp.Core.Models.Publisher; using WalletConnectSharp.Network.Models; using WalletConnectSharp.Sign; using WalletConnectSharp.Sign.Models; @@ -45,9 +45,8 @@ public WalletConnectCustomProvider(WalletConnectConfig config, IOperatingSystemM /// /// Connected client. - /// It's static to not destroy client session on logout/TerminateAsync, just disconnect instead. /// - public static WalletConnectSignClient SignClient { get; private set; } + public WalletConnectSignClient SignClient { get; private set; } /// /// Wallet Connect Core. @@ -78,18 +77,6 @@ public ValueTask WillStartAsync() private async Task Initialize() { - if (SignClient != null) - { - Core = (WalletConnectCore)SignClient.Core; - } - - if (Core != null && Core.Initialized) - { - WCLogger.Log("Core already initialized"); - - return; - } - WCLogger.Logger = new WCLogWriter(logWriter); Core = new WalletConnectCore(new CoreOptions() @@ -314,11 +301,15 @@ public async Task Request(T data, long? expiry = null) // if it's a registered method try and open wallet if (Session.Namespaces.Any(n => n.Value.Methods.Contains(method))) { - Core.Relayer.Events.ListenForOnce( - RelayerEvents.Publish, - (_, _) => + // if default wallet exists and redirect is true redirect user to default wallet + EventUtils.ListenOnce( + (sender, args) => { - // if default wallet exists and redirect is true redirect user to default wallet + if (args.Topic != topic) + { + return; + } + if (config.RedirectToWallet && config.DefaultWallet != null) { WCLogger.Log("Opening Default Wallet..."); @@ -329,7 +320,8 @@ public async Task Request(T data, long? expiry = null) { WCLogger.Log("No Default Wallet to Open"); } - }); + }, handler => Core.Relayer.Publisher.OnPublishedMessage += handler, + handler => Core.Relayer.Publisher.OnPublishedMessage -= handler); } return await SignClient.Request(topic, data, chainId, expiry); @@ -387,6 +379,10 @@ private FileSystemStorage BuildStorage() File.Delete(path); } + GC.Collect(); + + GC.WaitForPendingFinalizers(); + WCLogger.Log($"Wallet Connect Storage set to {path}"); return new FileSystemStorage(path); @@ -403,9 +399,19 @@ public async Task Disconnect() try { - await SignClient.Disconnect(Session.Topic, Error.FromErrorType(ErrorType.USER_DISCONNECTED)); + if (SignClient != null) + { + await SignClient.Disconnect(Session.Topic, Error.FromErrorType(ErrorType.USER_DISCONNECTED)); + } + + if (Core != null) + { + await Core.Storage.Clear(); + } + + SignClient?.Dispose(); - await Core.Storage.Clear(); + Core?.Dispose(); } catch (Exception e) { diff --git a/src/ChainSafe.Gaming.WalletConnect/WalletConnectSigner.cs b/src/ChainSafe.Gaming.WalletConnect/WalletConnectSigner.cs index 8d13f9baf..b1d92c98a 100644 --- a/src/ChainSafe.Gaming.WalletConnect/WalletConnectSigner.cs +++ b/src/ChainSafe.Gaming.WalletConnect/WalletConnectSigner.cs @@ -46,6 +46,9 @@ public async ValueTask WillStartAsync() return; } + // Try to Disconnect before reinitializing/connecting if there's an existing connection. + await walletConnectCustomProvider.Disconnect(); + // get address by connecting address = await walletConnectCustomProvider.Connect(); } diff --git a/src/UnitySampleProject/Assets/Samples/web3.unity SDK/2.5.0/Web3.Unity Samples/Scripts/Scenes/ExistingWalletLogin.cs b/src/UnitySampleProject/Assets/Samples/web3.unity SDK/2.5.0/Web3.Unity Samples/Scripts/Scenes/ExistingWalletLogin.cs index 08dcf0b0c..7b2878b26 100644 --- a/src/UnitySampleProject/Assets/Samples/web3.unity SDK/2.5.0/Web3.Unity Samples/Scripts/Scenes/ExistingWalletLogin.cs +++ b/src/UnitySampleProject/Assets/Samples/web3.unity SDK/2.5.0/Web3.Unity Samples/Scripts/Scenes/ExistingWalletLogin.cs @@ -39,7 +39,7 @@ public class ExistingWalletLogin : Login #if !UNITY_2022_1_OR_NEWER // Use a custom connection builder due to an issue fixed in version 2022 and above https://blog.unity.com/engine-platform/il2cpp-full-generic-sharing-in-unity-2022-1-beta. - private WalletConnectWebSocketBuilder connectionBuilder; + private NativeWebSocketConnectionBuilder connectionBuilder; #endif [Header("Wallet Connect")] [SerializeField] @@ -82,15 +82,15 @@ protected override IEnumerator Initialize() #if !UNITY_2022_1_OR_NEWER - connectionBuilder = FindObjectOfType(); + connectionBuilder = FindObjectOfType(); // Initialize custom web socket if it's not already. if (connectionBuilder == null) { GameObject webSocketBuilderObj = - new GameObject(nameof(WalletConnectWebSocketBuilder), typeof(WalletConnectWebSocketBuilder)); + new GameObject(nameof(NativeWebSocketConnectionBuilder), typeof(NativeWebSocketConnectionBuilder)); - connectionBuilder = webSocketBuilderObj.GetComponent(); + connectionBuilder = webSocketBuilderObj.GetComponent(); // keep web socket during scene unload DontDestroyOnLoad(webSocketBuilderObj);