Ethereum Unity3D Boilerplate Questions

No, the error message becomes available in the resp object if the status code is >=400.

My server already is on 0.0.339.

I indeed did trace the call, I’ll try to make a more detailed stack trace and HTTP response log.

That is correct. I basically tested by running in the editor and building it for Android. On the Android build, the session token differs, and the server says it won’t allow that as per the log.

1 Like

I will ask about this.

Thank you, I want to reproduce the behavior.

The code that sends the request from MobileLogin.cs is this:

        private async static Task<MoralisLoginTokenResponse> RequestLoginToken(string moralisServerUrl, string applicationId)
        {
            MoralisLoginTokenResponse result = null;

            MoralisLoginTokenRequest payload = new MoralisLoginTokenRequest()
            {
                _ApplicationId = applicationId
            };

            string data = JsonConvert.SerializeObject(payload);

            using (HttpClient client = new HttpClient())
            {
                client.BaseAddress = new Uri(moralisServerUrl);

                // Add an Accept header for JSON format.
                client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));

                StringContent content = new StringContent(data);
                // List data response.
                HttpResponseMessage response = client.PostAsync(TOKEN_REQUEST_URL, content).Result;  // Blocking call! Program will wait here until a response is received or a timeout occurs.

                if (response.IsSuccessStatusCode)
                {
                    // Parse the response body. 
                    string responseBody = await response.Content.ReadAsStringAsync();

                    result = JsonConvert.DeserializeObject<MoralisLoginTokenResponse>(responseBody);
                }
            }

            return result;
        }
    }

When execution reaches the check for whether it was successful or not:

client

  {System.Net.Http.HttpClient}
    BaseAddress: {https://ymg268vhppjv.usemoralis.com:2053/server}
    DefaultRequestHeaders: {Accept: application/json\r\n}
    MaxResponseContentBufferSize: 2147483647
    Timeout: {00:01:40}
    base_address: {https://ymg268vhppjv.usemoralis.com:2053/server}
    buffer_size: 2147483647
    cts: {System.Threading.CancellationTokenSource}
    disposeHandler: true
    disposed: false
    handler: {System.Net.Http.HttpClientHandler}
    headers: {Accept: application/json\r\n}
    timeout: {00:01:40}

content

  {System.Net.Http.StringContent}
    Headers: {Content-Type: text/plain; charset=utf-8\r\nContent-Length: 61\r\n}
    LoadedBufferLength: null
    buffer: null
    content: {byte[61]}
    count: 61
    disposed: false
    headers: {Content-Type: text/plain; charset=utf-8\r\nContent-Length: 61\r\n}
    offset: 0
    stream: null

data

"{\"_ApplicationId\":\"UzNkJCH6fPfgEQeCOQhfzAtIbLlAbEDbpN7g4oqa\"}"

response

  {StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:\r\n{\r\nDate: Sat, 05 Feb 2022 08:44:38 GMT\r\nConnection: keep-alive\r\nX-Powered-By: Express\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS\r\nAccess-Control-Allow-Headers: X-Parse-Master-Key, X-Parse-REST-API-Key, X-Parse-Javascript-Key, X-Parse-Application-Id, X-Parse-Client-Version, X-Parse-Session-Token, X-Requested-With, X-Parse-Revocable-Session, X-Parse-Request-Id, Content-Type, Pragma, Cache-Control, X-Parse-Installation-Id\r\nAccess-Control-Expose-Headers: X-Parse-Job-Status-Id, X-Parse-Push-Status-Id\r\nETag: W/\"3a-0JTlHXO2pC/ju0QshHRxP75EkI0\"\r\nCF-Cache-Status: DYNAMIC\r\nExpect-CT: max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"\r\nServer: cloudflare\r\nCF-RAY: 6d8adba458f33983-MAA\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: 58\r\n}}
    Content: {System.Net.Http.StreamContent}
    Headers: {Date: Sat, 05 Feb 2022 08:44:38 GMT\r\nConnection: keep-alive\r\nX-Powered-By: Express\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS\r\nAccess-Control-Allow-Headers: X-Parse-Master-Key, X-Parse-REST-API-Key, X-Parse-Javascript-Key, X-Parse-Application-Id, X-Parse-Client-Version, X-Parse-Session-Token, X-Requested-With, X-Parse-Revocable-Session, X-Parse-Request-Id, Content-Type, Pragma, Cache-Control, X-Parse-Installation-Id\r\nAccess-Control-Expose-Headers: X-Parse-Job-Status-Id, X-Parse-Push-Status-Id\r\nETag: W/\"3a-0JTlHXO2pC/ju0QshHRxP75EkI0\"\r\nCF-Cache-Status: DYNAMIC\r\nExpect-CT: max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"\r\nServer: cloudflare\r\nCF-RAY: 6d8adba458f33983-MAA\r\n}
    IsSuccessStatusCode: false
    ReasonPhrase: "Bad Request"
    RequestMessage: {Method: POST, RequestUri: 'https://ymg268vhppjv.usemoralis.com:2053/server/requestLoginToken', Version: 1.1, Content: System.Net.Http.StringContent, Headers:\r\n{\r\nAccept: application/json\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: 61\r\n}}
    StatusCode: BadRequest
    Version: {1.1}
    disposed: false
    headers: {Date: Sat, 05 Feb 2022 08:44:38 GMT\r\nConnection: keep-alive\r\nX-Powered-By: Express\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS\r\nAccess-Control-Allow-Headers: X-Parse-Master-Key, X-Parse-REST-API-Key, X-Parse-Javascript-Key, X-Parse-Application-Id, X-Parse-Client-Version, X-Parse-Session-Token, X-Requested-With, X-Parse-Revocable-Session, X-Parse-Request-Id, Content-Type, Pragma, Cache-Control, X-Parse-Installation-Id\r\nAccess-Control-Expose-Headers: X-Parse-Job-Status-Id, X-Parse-Push-Status-Id\r\nETag: W/\"3a-0JTlHXO2pC/ju0QshHRxP75EkI0\"\r\nCF-Cache-Status: DYNAMIC\r\nExpect-CT: max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"\r\nServer: cloudflare\r\nCF-RAY: 6d8adba458f33983-MAA\r\n}
    reasonPhrase: "Bad Request"
    statusCode: BadRequest
    version: null

Since the response is bad, the return value of this result is null, which propagates over to LogIn, causing it to in turn return null.

Is this failing on an IDE run or mobile? So far I have not been able to get it to fail.

I did try your server URL and Applications ID and did receive the 400 bad request.

The only think that looks odd to me is your server Url. However if that is what you get when you copy the link from your server I would not change it.

try this test server url: https://arw2wxg84h6b.moralishost.com:2053/server and application id: tNJatzsHirx4V2VAep6sc923OYGxvkpBeJttR7Ks (this is just for a test, please do not use this past trying a few times).

If this works for you then we need to talk to someone about your server.

and, sorry to ask - just making sure, the

address came from coping the server address from the admin panel:


correct?

Thanks,

David

1 Like

In this instance, this is indeed taken indirectly from the admin panel. The URL representation is what I received inside the function call, and the field passed down is indeed the same URL + /server.

This server works. However I think I connected using AVAX accidentally and I can’t find my token’s balance on MATIC Mumbai. I tried retrieving the native balance but it only worked once and kept timing out in 1 second.

I verified that different apps are not able to use the same log in by using the same account in a different app (Unity editor instance), which generates a different session token, and failing to interact with the Moralis server with a similar error:

2022-02-05T16:08:52.120Z - Error: Invalid session token
    at Object.getAuthForSessionToken (/moralis-server/lib/Auth.js:114:11)
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
1 Like

I posted the question - waiting on a replay

Please try copying your server address from your admin panel.

This behavior has not been seen in the JS SDK. I tried it myself on an Android device and Desktop, at the same time, using the same account and the same mobile wallet. Both apps authenticated properly for me. I also tried different devices and different walles and both instances connected as well.

Regards,

David

I just told you that, it’s the same thing.

They did authenticate but I was not able to call Cloud Functions without this error popping up on the second instance in each case.

I will verify this again with the ID and URL you’ve provided (assuming you’ve cloned my server).

EDIT: when I used https://arw2wxg84h6b.moralishost.com:2053/server and tNJatzsHirx4V2VAep6sc923OYGxvkpBeJttR7Ks on the first app (Unity game, in editor), the authentication was successful, but since I didn’t have access to the server logs I dropped a breakpoint in MoralisCloudFunctionService.cs:44 (v1.0.2) and got this as the response from the server: "{\"code\":209,\"error\":\"Invalid session token\"}".

If the server is an exact clone, it must have the auth details, and I verified this by seeing that MoralisInterface.IsLoggedIn was successful after running MoralisInterface.Initialize, so it is clear that it expects a different session token for each Cloud Function call. Is it then possible to retrieve this per app, or does this necessitate using a completely different method to verify a user when they’re calling a Cloud Function? Forgot that the user is often retrieved from the session token in memory or on disk.

Also, is there a way to log the user out (manually if need be), because atm the only way I can test this is by making new apps or builds.

Hello,
in order to call a function from unity to a smartcontract and change the state, can I use this line I found in AwardableController.cs : “string resp = await MoralisInterface.SendEvmTransactionAsync(“Rewards”, “mumbai”, “claimReward”, addr, gas, new HexBigInteger(“0x0”), pars);”
And I just have to put my parameters in the “()” ?
Thanks you for help.

1 Like

Please try running that and seeing if you get an output with a transaction ID that you can view on polygonscan.

Yes - see the Quit() method in the MainmenuScript.js file. this logs the user out of Moralis and Wallet Connect.

Yes - please look through the documentation for Nethereum to get a good understanding of how the underlying call is made. Nethereum has more functions that can be called in addition to the two I exposed.

I assume you meant .cs?

I have used this and it gets called whenever the app quits using Application.quitting but the user is not getting logged out – this is evident from me being able to playtest the games without repeating the login process.

        protected override async void Start()
        {
            base.Start();

            walletConnect = GetComponentInChildren<WalletConnect>();

            if (!Application.isPlaying) return;
            Application.quitting += Cleanup;

            authPanel.gameObject.SetActive(false);

            await MoralisInterface.Initialize(moralisAppId, moralisServerUri, new HostManifestData
            {
                Identifier = appName,
                Name = appName,
                ShortVersion = buildVersion,
                Version = buildVersion
            });

            if (MoralisInterface.IsLoggedIn())
            {
                Debug.Log(UserIsAlreadyLoggedInToMoralis);
            }
            else
            {
                authPanel.gameObject.SetActive(true);
            }

            async void Cleanup()
            {
                Debug.Log("QUITTING");

                await walletConnect.Session.Disconnect();
                walletConnect.CLearSession();
                await MoralisInterface.LogOutAsync();
            }
        }

I even tried moving the local function up and running it on Update on key press but in any case I receive the following errors. The first one appears whenever I quit the application in the editor by pressing the play button and triggering Application.quitting.

NullReferenceException: Object reference not set to an instance of an object
WalletConnectSharp.Unity.WalletConnect+<SaveOrDisconnect>d__58.MoveNext () (at D:/dev/Banzan/UltraCore/MoralisWeb3ApiSdk/WalletConnectSharp.Unity/WalletConnect.cs:413)
--- End of stack trace from previous location where exception was thrown ---
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Runtime.CompilerServices.TaskAwaiter.GetResult () (at <695d1cc93cca45069c528c15c9fdd749>:0)
WalletConnectSharp.Unity.WalletConnect+<OnApplicationQuit>d__56.MoveNext () (at D:/dev/Banzan/UltraCore/MoralisWeb3ApiSdk/WalletConnectSharp.Unity/WalletConnect.cs:396)
--- End of stack trace from previous location where exception was thrown ---
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.<ThrowAsync>b__6_0 (System.Object state) (at <695d1cc93cca45069c528c15c9fdd749>:0)
UnityEngine.UnitySynchronizationContext+WorkRequest.Invoke () (at <d3b66f0ad4e34a55b6ef91ab84878193>:0)
UnityEngine.UnitySynchronizationContext:ExecuteTasks()

This error appears in both cases (on quitting event trigger and when triggered on key press)

NullReferenceException: Object reference not set to an instance of an object
Banzan.UltraCore.Cryptofied.MoralisLoginUI+<Cleanup>d__11.MoveNext () (at D:/dev/Banzan/UltraCore/Runtime/Cryptofied/MoralisLoginUI.cs:90)
--- End of stack trace from previous location where exception was thrown ---
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.<ThrowAsync>b__6_0 (System.Object state) (at <695d1cc93cca45069c528c15c9fdd749>:0)
UnityEngine.UnitySynchronizationContext+WorkRequest.Invoke () (at <d3b66f0ad4e34a55b6ef91ab84878193>:0)
UnityEngine.UnitySynchronizationContext:ExecuteTasks()

of course.

I assume you mean login in. Not sure what to say - I know with the demo if I exit without hitting the Quit button. I can start the game and play again without authenticating (still authenticated).

However if I click the Quit button and then re-start I am asked to Authenticate. In the demo the Authenticate button is only shown if MoralisInterface.IsLoggedIn() returns false.

This code:

async void Cleanup()
            {
                Debug.Log("QUITTING");

                await walletConnect.Session.Disconnect();
                walletConnect.CLearSession();
                await MoralisInterface.LogOutAsync();
            }

looks valid to me. So you are saying this is called and then when you re-start, on the same device, it acts as if the user is still logged in (MoralisInterface.IsLoggedIn() returns true)?

Yes, that’s correct. If this is meant to complete the logout process, it’s not done that in my case wherever I’ve used this.

I assume this is the expected behaviour in the Examples script because it doesn’t use Application.quitting. Well in this case it doesn’t quite work that way. I’d expect to be logged out each time but I’m not.

Any idea about whether I should be able to use the same Moralis server URL and app ID in multiple Unity apps to access the Moralis database?

Is it possible the Cleanup method is not being called as an async method? If you debug through this function do you hit the code that removes the prefab session (in the Wallet Connect code) and then the code that deletes the local datastore entry for the user (in the Moralis code)?

1 Like

I’m not quite sure what you mean. I think I get that CLearSession unsets the PlayerPrefs key but I’m not sure about any prefabs being removed.

If it makes sense the game object containing the WalletConnect script is a child of an object which is set as DontDestroyOnLoad.

Sorry that should have been PlayerPrefs entry not prefab.

What about the Moralis LogoutAsync? Is that called?

No, I guess the code throws NPE before it reaches LogOutAsync.
The Wallet Connect Session itself is null.