From 2e468838b68f7b504068d2bba65e237455898180 Mon Sep 17 00:00:00 2001 From: Kai Hirota <34954529+kaihirota@users.noreply.github.com> Date: Fri, 11 Oct 2024 14:59:11 +1100 Subject: [PATCH 1/4] update --- Assets/Shared/Scripts/SequenceManager.cs | 3 - Assets/Shared/Scripts/UI/MintScreen.cs | 138 +++++++++++++++++- .../Shared/Scripts/UI/UnlockedSkinScreen.cs | 54 +++++-- contracts/contracts/RunnerToken.sol | 2 +- mint-backend/dest/index.js | 29 ++-- mint-backend/package.json | 23 +-- mint-backend/src/index.ts | 33 +++-- mint-backend/tsconfig.json | 2 +- 8 files changed, 228 insertions(+), 56 deletions(-) diff --git a/Assets/Shared/Scripts/SequenceManager.cs b/Assets/Shared/Scripts/SequenceManager.cs index c6a721611..75a0f0199 100644 --- a/Assets/Shared/Scripts/SequenceManager.cs +++ b/Assets/Shared/Scripts/SequenceManager.cs @@ -5,9 +5,6 @@ using HyperCasual.Runner; using UnityEngine; using UnityEngine.SceneManagement; -using System; -using System.Collections; -using UnityEngine; namespace HyperCasual.Gameplay { diff --git a/Assets/Shared/Scripts/UI/MintScreen.cs b/Assets/Shared/Scripts/UI/MintScreen.cs index c13f50f8b..8abec9090 100644 --- a/Assets/Shared/Scripts/UI/MintScreen.cs +++ b/Assets/Shared/Scripts/UI/MintScreen.cs @@ -5,6 +5,10 @@ using System; using System.Collections.Generic; using System.Threading; +using System.Net.Http; +using Immutable.Passport; +using Cysharp.Threading.Tasks; +using System.Numerics; namespace HyperCasual.Runner { @@ -33,6 +37,10 @@ public class MintScreen : View [SerializeField] HyperCasualButton m_WalletButton; + // If there's an error minting, these values will be used when the player clicks the "Try again" button + private bool mintedFox = false; + private bool mintedCoins = false; + public void OnEnable() { // Set listener to 'Next' button @@ -47,10 +55,14 @@ public void OnEnable() m_WalletButton.RemoveListener(OnWalletClicked); m_WalletButton.AddListener(OnWalletClicked); + // Reset values + mintedFox = false; + mintedCoins = false; + Mint(); } - private void Mint() + private async void Mint() { try { @@ -59,12 +71,27 @@ private void Mint() ShowError(false); ShowNextButton(false); - // Mint + // Mint fox if not minted yet + if (!mintedFox) + { + mintedFox = await MintFox(); + } + // Mint coins if not minted yet + if (!mintedCoins) + { + mintedCoins = await MintCoins(); + } - ShowMintedMessage(); + // Show minted message if minted both fox and coins successfully + if (mintedFox && mintedCoins) + { + ShowMintedMessage(); + } ShowLoading(false); - ShowError(false); - ShowNextButton(true); + // Show error if failed to mint fox or coins + ShowError(!mintedFox || !mintedCoins); + // Show next button is minted both fox and coins successfully + ShowNextButton(mintedFox && mintedCoins); } catch (Exception ex) { @@ -76,6 +103,101 @@ private void Mint() } } + /// + /// Gets the wallet address of the player. + /// + private async UniTask GetWalletAddress() + { + List accounts = await Passport.Instance.ZkEvmRequestAccounts(); + return accounts[0]; // Get the first wallet address + } + + /// + /// Mints a fox (i.e. Immutable Runner Fox) to the player's wallet + /// + /// True if minted a fox successfully to player's wallet. Otherwise, false. + private async UniTask MintFox() + { + Debug.Log("Minting fox..."); + try + { + string address = await GetWalletAddress(); // Get the player's wallet address to mint the fox to + + if (address != null) + { + var nvc = new List> + { + // Set 'to' to the player's wallet address + new KeyValuePair("to", address) + }; + using var client = new HttpClient(); + string url = $"http://localhost:3000/mint/fox"; // Endpoint to mint fox + using var req = new HttpRequestMessage(HttpMethod.Post, url) { Content = new FormUrlEncodedContent(nvc) }; + using var res = await client.SendAsync(req); + + string content = await res.Content.ReadAsStringAsync(); + Debug.Log($"Mint fox response: {content}"); + + return res.IsSuccessStatusCode; + } + + return false; + } + catch (Exception ex) + { + Debug.Log($"Failed to mint fox: {ex.Message}"); + return false; + } + } + + /// + /// Mints collected coins (i.e. Immutable Runner Token) to the player's wallet + /// + /// True if minted coins successfully to player's wallet. Otherwise, false. + private async UniTask MintCoins() + { + Debug.Log("Minting coins..."); + try + { + int coinsCollected = GetNumCoinsCollected(); // Get number of coins collected + if (coinsCollected == 0) // Don't mint any coins if player did not collect any + { + return true; + } + + string address = await GetWalletAddress(); // Get the player's wallet address to mint the coins to + if (address != null) + { + // Calculate the quantity to mint + // Need to take into account Immutable Runner Token decimal value i.e. 18 + BigInteger quantity = BigInteger.Multiply(new BigInteger(coinsCollected), BigInteger.Pow(10, 18)); + Debug.Log($"Quantity: {quantity}"); + var nvc = new List> { + // Set 'to' to the player's wallet address + new KeyValuePair("to", address), + // Set 'quanity' to the number of coins collected + new KeyValuePair("quantity", quantity.ToString()) + }; + using var client = new HttpClient(); + string url = $"http://localhost:3000/mint/token"; // Endpoint to mint token + using var req = new HttpRequestMessage(HttpMethod.Post, url) { Content = new FormUrlEncodedContent(nvc) }; + using var res = await client.SendAsync(req); + + string content = await res.Content.ReadAsStringAsync(); + Debug.Log($"Mint tokens response: {content}"); + + return res.IsSuccessStatusCode; + } + + return false; + } + catch (Exception ex) + { + Debug.Log($"Failed to mint coins: {ex.Message}"); + return false; + } + } + private void OnNextButtonClicked() { m_NextEvent.Raise(); @@ -141,8 +263,12 @@ private void ShowMintedMessage() } } - private void OnWalletClicked() + private async void OnWalletClicked() { + // Get the player's wallet address to mint the fox to + string address = await GetWalletAddress(); + // Show the player's tokens on the block explorer page. + Application.OpenURL($"https://explorer.testnet.immutable.com/address/{address}?tab=tokens"); } } } diff --git a/Assets/Shared/Scripts/UI/UnlockedSkinScreen.cs b/Assets/Shared/Scripts/UI/UnlockedSkinScreen.cs index 0b563d450..51949f5d6 100644 --- a/Assets/Shared/Scripts/UI/UnlockedSkinScreen.cs +++ b/Assets/Shared/Scripts/UI/UnlockedSkinScreen.cs @@ -5,7 +5,27 @@ using UnityEngine.UI; using System; using System.Collections.Generic; +using System.Net.Http; +using System.Threading; using System.Threading.Tasks; +using Cysharp.Threading.Tasks; +using Immutable.Passport; +using Immutable.Passport.Model; + + +[Serializable] +public class GetAssetsResponse +{ + public Asset[] result; +} + +[Serializable] +public class Asset +{ + public string id; + public string token_id; + public string token_address; +} namespace HyperCasual.Runner { @@ -45,7 +65,7 @@ public CraftSkinState? CraftState get => m_CraftState; set { - CraftState = value; + m_CraftState = value; switch (m_CraftState) { case CraftSkinState.Crafting: @@ -113,18 +133,32 @@ public async void OnEnable() private async void Craft() { - CraftState = CraftSkinState.Crafting; + m_CraftState = CraftSkinState.Crafting; // Burn tokens and mint a new skin i.e. crafting a skin - await Task.Delay(TimeSpan.FromSeconds(5)); + const string runnerTokenContractAddress = "0xd14983206B7f2348b976878cCF097E55E1461977"; - CraftState = CraftSkinState.Crafted; + try { + TransactionReceiptResponse response = await Passport.Instance.ZkEvmSendTransactionWithConfirmation(new TransactionRequest() + { + to = runnerTokenContractAddress, // Immutable Runner Token contract address + data = "0x1e957f1e", // Call craftSkin() in the contract + value = "0" + }); + Debug.Log($"Craft transaction hash: {response.transactionHash}"); - // If successfully crafted skin and this screen is visible, go to collect skin screen - // otherwise it will be picked in the OnEnable function above when this screen reappears - if (m_CraftState == CraftSkinState.Crafted && gameObject.active) - { - CollectSkin(); + m_CraftState = response.status != "1" ? CraftSkinState.Failed : CraftSkinState.Crafted; + + + // If successfully crafted skin and this screen is visible, go to collect skin screen + // otherwise it will be picked in the OnEnable function above when this screen reappears + if (m_CraftState == CraftSkinState.Crafted && gameObject.active) + { + CollectSkin(); + } + } catch (Exception ex) { + Debug.Log($"Failed to craft skin: {ex.Message}"); + m_CraftState = CraftSkinState.Failed; } } @@ -136,9 +170,9 @@ private void CollectSkin() private void OnCraftButtonClicked() { - m_NextLevelEvent.Raise(); // Craft in the background, while the player plays the next level Craft(); + // m_NextLevelEvent.Raise(); } private void OnTryAgainButtonClicked() diff --git a/contracts/contracts/RunnerToken.sol b/contracts/contracts/RunnerToken.sol index 86e10ade6..df99d9be9 100644 --- a/contracts/contracts/RunnerToken.sol +++ b/contracts/contracts/RunnerToken.sol @@ -24,7 +24,7 @@ contract RunnerToken is ERC20, ERC20Burnable, MintingAccessControl { _skinContract = ImmutableERC721(skinContractAddr); // Uncomment the line below to grant minter role to contract deployer - // _grantRole(MINTER_ROLE, msg.sender); + _grantRole(MINTER_ROLE, msg.sender); } // Mints the number of tokens specified diff --git a/mint-backend/dest/index.js b/mint-backend/dest/index.js index 2748d0bcf..d0c499cd0 100644 --- a/mint-backend/dest/index.js +++ b/mint-backend/dest/index.js @@ -7,6 +7,7 @@ const express_1 = __importDefault(require("express")); const cors_1 = __importDefault(require("cors")); const http_1 = __importDefault(require("http")); const ethers_1 = require("ethers"); +const providers_1 = require("@ethersproject/providers"); const morgan_1 = __importDefault(require("morgan")); const dotenv_1 = __importDefault(require("dotenv")); dotenv_1.default.config(); @@ -16,7 +17,7 @@ app.use(express_1.default.urlencoded({ extended: false })); // Parse request app.use(express_1.default.json()); // Handle JSON app.use((0, cors_1.default)()); // Enable CORS const router = express_1.default.Router(); -const zkEvmProvider = new ethers_1.JsonRpcProvider('https://rpc.testnet.immutable.com'); +const zkEvmProvider = new providers_1.JsonRpcProvider('https://rpc.testnet.immutable.com'); // Contract addresses const foxContractAddress = process.env.FOX_CONTRACT_ADDRESS; const tokenContractAddress = process.env.TOKEN_CONTRACT_ADDRESS; @@ -29,12 +30,13 @@ const gasOverrides = { }; // Mint Immutable Runner Fox router.post('/mint/fox', async (req, res) => { + console.log(req.body); try { if (foxContractAddress && privateKey) { // Get the address to mint the fox to let to = req.body.to ?? null; // Get the quantity to mint if specified, default is one - let quantity = parseInt(req.body.quantity ?? "1"); + let quantity = parseInt(req.body.quantity ?? '1'); // Connect to wallet with minter role const signer = new ethers_1.Wallet(privateKey).connect(zkEvmProvider); // Specify the function to call @@ -44,25 +46,29 @@ router.post('/mint/fox', async (req, res) => { // Mints the number of tokens specified const tx = await contract.mintByQuantity(to, quantity, gasOverrides); await tx.wait(); - return res.status(200).json({}); + res.writeHead(200); + res.end(JSON.stringify({ message: "Minted foxes" })); } else { - return res.status(500).json({}); + res.writeHead(400); + res.end(JSON.stringify({ message: "Failed to mint" })); } } catch (error) { console.log(error); - return res.status(400).json({ message: "Failed to mint to user" }); + res.writeHead(500); + res.end(JSON.stringify({ message: error })); } }); // Mint Immutable Runner Token router.post('/mint/token', async (req, res) => { + console.log(req.body); try { if (tokenContractAddress && privateKey) { // Get the address to mint the token to let to = req.body.to ?? null; // Get the quantity to mint if specified, default is one - let quantity = BigInt(req.body.quantity ?? "1"); + let quantity = BigInt(req.body.quantity ?? '1'); // Connect to wallet with minter role const signer = new ethers_1.Wallet(privateKey).connect(zkEvmProvider); // Specify the function to call @@ -72,16 +78,19 @@ router.post('/mint/token', async (req, res) => { // Mints the number of tokens specified const tx = await contract.mint(to, quantity, gasOverrides); await tx.wait(); - return res.status(200).json({}); + res.writeHead(200); + res.end(JSON.stringify({ message: "Minted ERC20 tokens" })); } else { - return res.status(500).json({}); + res.writeHead(400); + res.end(JSON.stringify({ message: "Failed to mint ERC20 tokens" })); } } catch (error) { console.log(error); - return res.status(400).json({ message: "Failed to mint to user" }); + res.writeHead(500); + res.end(JSON.stringify({ message: error })); } }); app.use('/', router); -http_1.default.createServer(app).listen(3000, () => console.log(`Listening on port 3000`)); +http_1.default.createServer(app).listen(3000, () => console.log('Listening on port 3000')); diff --git a/mint-backend/package.json b/mint-backend/package.json index 71406e648..61c35f30a 100644 --- a/mint-backend/package.json +++ b/mint-backend/package.json @@ -10,24 +10,25 @@ }, "devDependencies": { "@types/cors": "^2.8.13", - "@types/express": "^4.17.17", + "@types/express": "^5.0.0", "@types/morgan": "^1.9.9", - "@typescript-eslint/eslint-plugin": "^7.11.0", - "eslint": "^8.56.0", + "@typescript-eslint/eslint-plugin": "^8.8.0", + "eslint": "^9.11.1", "eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb-typescript": "^18.0.0", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jsx-a11y": "^6.8.0", - "eslint-plugin-react": "^7.34.2", + "eslint-plugin-import": "^2.30.0", + "eslint-plugin-jsx-a11y": "^6.10.0", + "eslint-plugin-react": "^7.37.1", "eslint-plugin-react-hooks": "^4.6.2", - "typescript": "^5.4.3", - "typescript-eslint": "^7.11.0" + "typescript": "^5.6.2", + "typescript-eslint": "^8.8.0" }, "dependencies": { + "@ethersproject/providers": "^5.7.2", + "@imtbl/sdk": "1.55.0", "cors": "^2.8.5", "dotenv": "^16.4.5", - "ethers": "^6.11.1", - "express": "^4.20.0", + "express": "^5.0.0", "morgan": "^1.10.0" } -} \ No newline at end of file +} diff --git a/mint-backend/src/index.ts b/mint-backend/src/index.ts index 4d4c11037..61e75b77f 100644 --- a/mint-backend/src/index.ts +++ b/mint-backend/src/index.ts @@ -6,7 +6,8 @@ import express, { } from 'express'; import cors from 'cors'; import http from 'http'; -import { JsonRpcProvider, Wallet, Contract } from 'ethers'; +import { Wallet, Contract } from 'ethers'; +import { JsonRpcProvider } from '@ethersproject/providers'; import morgan from 'morgan'; import dotenv from 'dotenv'; @@ -35,6 +36,7 @@ const gasOverrides = { // Mint Immutable Runner Fox router.post('/mint/fox', async (req: Request, res: Response) => { + console.log(req.body); try { if (foxContractAddress && privateKey) { // Get the address to mint the fox to @@ -54,20 +56,22 @@ router.post('/mint/fox', async (req: Request, res: Response) => { const tx = await contract.mintByQuantity(to, quantity, gasOverrides); await tx.wait(); - return res.status(200).json({}); + res.writeHead(200); + res.end(JSON.stringify({ message: "Minted foxes" })); } else { - return res.status(500).json({}); + res.writeHead(400); + res.end(JSON.stringify({ message: "Failed to mint" })); } - } catch (error) { console.log(error); - return res.status(400).json({ message: 'Failed to mint to user' }); + res.writeHead(500); + res.end(JSON.stringify({ message: error })); } -}, -); +}); // Mint Immutable Runner Token router.post('/mint/token', async (req: Request, res: Response) => { + console.log(req.body); try { if (tokenContractAddress && privateKey) { // Get the address to mint the token to @@ -87,21 +91,22 @@ router.post('/mint/token', async (req: Request, res: Response) => { const tx = await contract.mint(to, quantity, gasOverrides); await tx.wait(); - return res.status(200).json({}); + res.writeHead(200); + res.end(JSON.stringify({ message: "Minted ERC20 tokens" })); } else { - return res.status(500).json({}); + res.writeHead(400); + res.end(JSON.stringify({ message: "Failed to mint ERC20 tokens" })); } - } catch (error) { console.log(error); - return res.status(400).json({ message: 'Failed to mint to user' }); + res.writeHead(500); + res.end(JSON.stringify({ message: error })); } -}, -); +}); app.use('/', router); http.createServer(app).listen( 3000, () => console.log('Listening on port 3000'), -); \ No newline at end of file +); diff --git a/mint-backend/tsconfig.json b/mint-backend/tsconfig.json index c8b808b5b..6fd82388b 100644 --- a/mint-backend/tsconfig.json +++ b/mint-backend/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "ES2020", - "module": "Node16", + "module": "CommonJS", "esModuleInterop": true, "outDir": "./dest", "skipLibCheck": true, From 1d9c501b6cc19e1fcb3fee83e240086bea10a81f Mon Sep 17 00:00:00 2001 From: Kai Hirota <34954529+kaihirota@users.noreply.github.com> Date: Mon, 14 Oct 2024 11:26:32 +1100 Subject: [PATCH 2/4] add clientID --- Assets/Shared/Scripts/UI/MainMenu.cs | 47 ++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/Assets/Shared/Scripts/UI/MainMenu.cs b/Assets/Shared/Scripts/UI/MainMenu.cs index 39c0643c0..a6bba2ed6 100644 --- a/Assets/Shared/Scripts/UI/MainMenu.cs +++ b/Assets/Shared/Scripts/UI/MainMenu.cs @@ -4,6 +4,7 @@ using UnityEngine; using UnityEngine.UI; using TMPro; +using Immutable.Passport; namespace HyperCasual.Runner { @@ -23,7 +24,9 @@ public class MainMenu : View [SerializeField] GameObject m_Loading; - void OnEnable() + Passport passport; + + async void OnEnable() { ShowLoading(true); @@ -34,8 +37,41 @@ void OnEnable() m_LogoutButton.RemoveListener(OnLogoutButtonClick); m_LogoutButton.AddListener(OnLogoutButtonClick); + // Initialise Passport + string clientId = "Jf3dcKYrYUC6MLCMsDsOvKGoirlAzGJR"; + string environment = Immutable.Passport.Model.Environment.SANDBOX; + string redirectUri = null; + string logoutUri = null; + +#if (UNITY_ANDROID && !UNITY_EDITOR_WIN) || (UNITY_IPHONE && !UNITY_EDITOR_WIN) || UNITY_STANDALONE_OSX + redirectUri = "immutablerunner://callback"; + logoutUri = "immutablerunner://logout"; +#endif + passport = await Passport.Init(clientId, environment, redirectUri, logoutUri); + + // Check if the player is supposed to be logged in and if there are credentials saved + if (SaveManager.Instance.IsLoggedIn && await Passport.Instance.HasCredentialsSaved()) + { + // Try to log in using saved credentials + bool success = await Passport.Instance.Login(useCachedSession: true); + // Update the login flag + SaveManager.Instance.IsLoggedIn = success; + // Set up wallet if successful + if (success) + { + await Passport.Instance.ConnectEvm(); + await Passport.Instance.ZkEvmRequestAccounts(); + } + } + else + { + // No saved credentials to re-login the player, reset the login flag + SaveManager.Instance.IsLoggedIn = false; + } + ShowLoading(false); - ShowStartButton(true); + // Show the logout button if the player is logged in + ShowLogoutButton(SaveManager.Instance.IsLoggedIn); } void OnDisable() @@ -49,7 +85,7 @@ void OnStartButtonClick() AudioManager.Instance.PlayEffect(SoundID.ButtonSound); } - void OnLogoutButtonClick() + async void OnLogoutButtonClick() { try { @@ -59,6 +95,11 @@ void OnLogoutButtonClick() ShowLoading(true); // Logout +#if (UNITY_ANDROID && !UNITY_EDITOR_WIN) || (UNITY_IPHONE && !UNITY_EDITOR_WIN) || UNITY_STANDALONE_OSX + await passport.LogoutPKCE(); +#else + await passport.Logout(); +#endif // Reset the login flag SaveManager.Instance.IsLoggedIn = false; From 4aba9713a7d59d680dc81d13600df5dcea18e358 Mon Sep 17 00:00:00 2001 From: Kai Hirota <34954529+kaihirota@users.noreply.github.com> Date: Mon, 14 Oct 2024 13:17:59 +1100 Subject: [PATCH 3/4] fix login --- .../Shared/Scripts/UI/LevelCompleteScreen.cs | 126 +++++++++++++++++- Assets/Shared/Scripts/UI/MainMenu.cs | 8 -- 2 files changed, 119 insertions(+), 15 deletions(-) diff --git a/Assets/Shared/Scripts/UI/LevelCompleteScreen.cs b/Assets/Shared/Scripts/UI/LevelCompleteScreen.cs index e98d46263..ec9340835 100644 --- a/Assets/Shared/Scripts/UI/LevelCompleteScreen.cs +++ b/Assets/Shared/Scripts/UI/LevelCompleteScreen.cs @@ -5,6 +5,10 @@ using UnityEngine.UI; using System; using System.Collections.Generic; +using Immutable.Passport; +using Cysharp.Threading.Tasks; +using System.Numerics; +using System.Net.Http; namespace HyperCasual.Runner { @@ -102,13 +106,13 @@ public int CoinCount } } - public void OnEnable() + public async void OnEnable() { // Set listener to 'Next' button m_NextButton.RemoveListener(OnNextButtonClicked); m_NextButton.AddListener(OnNextButtonClicked); - // Set listener to "Continue with Passport" button + // Set listener to "Continue with Passport" button m_ContinuePassportButton.RemoveListener(OnContinueWithPassportButtonClicked); m_ContinuePassportButton.AddListener(OnContinueWithPassportButtonClicked); @@ -116,20 +120,128 @@ public void OnEnable() m_TryAgainButton.RemoveListener(OnTryAgainButtonClicked); m_TryAgainButton.AddListener(OnTryAgainButtonClicked); - ShowNextButton(true); + ShowError(false); + ShowLoading(false); + + // If player is logged into Passport mint coins to player + if (SaveManager.Instance.IsLoggedIn) + { + // Mint collected coins to player + await MintCoins(); + } + else + { + // Show 'Next' button if player is already logged into Passport + ShowNextButton(SaveManager.Instance.IsLoggedIn); + // Show "Continue with Passport" button if the player is not logged into Passport + ShowContinueWithPassportButton(!SaveManager.Instance.IsLoggedIn); + } } - private void OnContinueWithPassportButtonClicked() + /// + /// Mints collected coins (i.e. Immutable Runner Token) to the player's wallet + /// + private async UniTask MintCoins() { + // This function is similar to MintCoins() in MintScreen.cs. Consider refactoring duplicate code in production. + Debug.Log("Minting coins..."); + bool success = false; + + // Show loading + ShowLoading(true); + ShowNextButton(false); + ShowError(false); + + try + { + // Don't mint any coins if player did not collect any + if (m_CoinCount == 0) + { + success = true; + } + else + { + // Get the player's wallet address to mint the coins to + List accounts = await Passport.Instance.ZkEvmRequestAccounts(); + string address = accounts[0]; + if (address != null) + { + // Calculate the quantity to mint + // Need to take into account Immutable Runner Token decimal value i.e. 18 + BigInteger quantity = BigInteger.Multiply(new BigInteger(m_CoinCount), BigInteger.Pow(10, 18)); + Debug.Log($"Quantity: {quantity}"); + var nvc = new List> + { + // Set 'to' to the player's wallet address + new KeyValuePair("to", address), + // Set 'quanity' to the number of coins collected + new KeyValuePair("quantity", quantity.ToString()) + }; + using var client = new HttpClient(); + string url = $"http://localhost:3000/mint/token"; // Endpoint to mint token + using var req = new HttpRequestMessage(HttpMethod.Post, url) { Content = new FormUrlEncodedContent(nvc) }; + using var res = await client.SendAsync(req); + success = res.IsSuccessStatusCode; + } + } + } + catch (Exception ex) + { + Debug.Log($"Failed to mint coins: {ex.Message}"); + } + + ShowLoading(false); + ShowNextButton(success); + ShowError(!success); } - private void OnTryAgainButtonClicked() + private async void OnContinueWithPassportButtonClicked() { + try + { + // Show loading + ShowContinueWithPassportButton(false); + ShowLoading(true); + + // Log into Passport + await Passport.Instance.Login(); + + // Successfully logged in + // Save a persistent flag in the game that the player is logged in + SaveManager.Instance.IsLoggedIn = true; + // Show 'Next' button + ShowNextButton(true); + ShowLoading(false); + // Take the player to the Setup Wallet screen + m_SetupWalletEvent.Raise(); + } + catch (Exception ex) + { + Debug.Log($"Failed to log into Passport: {ex.Message}"); + // Show Continue with Passport button again + ShowContinueWithPassportButton(true); + ShowLoading(false); + } + } + + private async void OnTryAgainButtonClicked() + { + await MintCoins(); } private void OnNextButtonClicked() { - m_NextLevelEvent.Raise(); + // Check if the player is already using a new skin + if (!SaveManager.Instance.UseNewSkin) + { + // Player is not using a new skin, take player to Unlocked Skin screen + m_UnlockedSkinEvent.Raise(); + } + else + { + // Player is already using a new skin, take player to the next level + m_NextLevelEvent.Raise(); + } } private void ShowCompletedContainer(bool show) @@ -171,4 +283,4 @@ void DisplayCoins(int count) } } } -} +} \ No newline at end of file diff --git a/Assets/Shared/Scripts/UI/MainMenu.cs b/Assets/Shared/Scripts/UI/MainMenu.cs index a6bba2ed6..970fa6ed5 100644 --- a/Assets/Shared/Scripts/UI/MainMenu.cs +++ b/Assets/Shared/Scripts/UI/MainMenu.cs @@ -43,10 +43,6 @@ async void OnEnable() string redirectUri = null; string logoutUri = null; -#if (UNITY_ANDROID && !UNITY_EDITOR_WIN) || (UNITY_IPHONE && !UNITY_EDITOR_WIN) || UNITY_STANDALONE_OSX - redirectUri = "immutablerunner://callback"; - logoutUri = "immutablerunner://logout"; -#endif passport = await Passport.Init(clientId, environment, redirectUri, logoutUri); // Check if the player is supposed to be logged in and if there are credentials saved @@ -95,11 +91,7 @@ async void OnLogoutButtonClick() ShowLoading(true); // Logout -#if (UNITY_ANDROID && !UNITY_EDITOR_WIN) || (UNITY_IPHONE && !UNITY_EDITOR_WIN) || UNITY_STANDALONE_OSX - await passport.LogoutPKCE(); -#else await passport.Logout(); -#endif // Reset the login flag SaveManager.Instance.IsLoggedIn = false; From 35a317dea7d618bc4dcdd8b7ec071e420d0a1ba7 Mon Sep 17 00:00:00 2001 From: Kai Hirota <34954529+kaihirota@users.noreply.github.com> Date: Wed, 23 Oct 2024 12:59:47 +1100 Subject: [PATCH 4/4] fix --- Assets/Shared/Scripts/UI/SetupWalletScreen.cs | 7 +++-- Packages/manifest.json | 2 ++ Packages/packages-lock.json | 26 ++++++++++++++++++- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/Assets/Shared/Scripts/UI/SetupWalletScreen.cs b/Assets/Shared/Scripts/UI/SetupWalletScreen.cs index f8cf64f94..26b188426 100644 --- a/Assets/Shared/Scripts/UI/SetupWalletScreen.cs +++ b/Assets/Shared/Scripts/UI/SetupWalletScreen.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Threading; +using Immutable.Passport; namespace HyperCasual.Runner { @@ -44,7 +45,7 @@ public void OnEnable() SetupWallet(); } - private void SetupWallet() + private async void SetupWallet() { try { @@ -54,6 +55,8 @@ private void SetupWallet() ShowSuccess(false); // Set up wallet + await Passport.Instance.ConnectEvm(); + await Passport.Instance.ZkEvmRequestAccounts(); m_Title.text = "Your wallet has been successfully set up!"; ShowLoading(false); @@ -72,7 +75,7 @@ private void SetupWallet() private void OnNextButtonClicked() { - m_NextEvent.Raise(); + m_MintEvent.Raise(); } private void ShowNextButton(bool show) diff --git a/Packages/manifest.json b/Packages/manifest.json index 8e0ea5080..46170b4f9 100644 --- a/Packages/manifest.json +++ b/Packages/manifest.json @@ -1,5 +1,7 @@ { "dependencies": { + "com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask", + "com.immutable.passport": "https://github.com/immutable/unity-immutable-sdk.git?path=/src/Packages/Passport", "com.unity.collab-proxy": "2.0.4", "com.unity.ide.rider": "3.0.21", "com.unity.ide.visualstudio": "2.0.18", diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index c439fc2fa..3bb586d1b 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -1,5 +1,22 @@ { "dependencies": { + "com.cysharp.unitask": { + "version": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask", + "depth": 0, + "source": "git", + "dependencies": {}, + "hash": "f9fd769be7c634610f2a61aa914a1a55f34740e1" + }, + "com.immutable.passport": { + "version": "https://github.com/immutable/unity-immutable-sdk.git?path=/src/Packages/Passport", + "depth": 0, + "source": "git", + "dependencies": { + "com.unity.nuget.newtonsoft-json": "3.2.0", + "com.cysharp.unitask": "2.3.3" + }, + "hash": "9a3384926b73629516fc28c940a3ce4847edc503" + }, "com.unity.burst": { "version": "1.8.4", "depth": 1, @@ -81,6 +98,13 @@ "dependencies": {}, "url": "https://packages.unity.com" }, + "com.unity.nuget.newtonsoft-json": { + "version": "3.2.0", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, "com.unity.postprocessing": { "version": "3.2.2", "depth": 0, @@ -159,9 +183,9 @@ "depth": 0, "source": "registry", "dependencies": { + "com.unity.modules.audio": "1.0.0", "com.unity.modules.director": "1.0.0", "com.unity.modules.animation": "1.0.0", - "com.unity.modules.audio": "1.0.0", "com.unity.modules.particlesystem": "1.0.0" }, "url": "https://packages.unity.com"