diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml
new file mode 100644
index 00000000..c24cc792
--- /dev/null
+++ b/.github/workflows/build-test.yml
@@ -0,0 +1,105 @@
+name: Build and test
+
+on:
+  push:
+    branches: [main]
+  pull_request:
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+  
+jobs:
+  test:
+    name: Test sample game 🧪
+    runs-on: ubuntu-latest-4-cores
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+        with:
+          lfs: true
+
+      - uses: actions/cache@v3
+        with:
+          path: Library
+          key: Library-${{ hashFiles('Assets/**', 'Packages/**', 'ProjectSettings/**') }}
+          restore-keys: |
+            Library-
+
+      - name: Run tests
+        uses: game-ci/unity-test-runner@v4
+        env:
+          UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
+          UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
+          UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
+        with:
+          githubToken: ${{ secrets.GITHUB_TOKEN }}
+          
+  ubuntu:
+    name: Build for ${{ matrix.targetPlatform }}
+    runs-on: ubuntu-latest-8-cores
+    strategy:
+      fail-fast: false
+      matrix:
+        targetPlatform:
+#          - iOS
+          - Android
+#          - WebGL
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          lfs: true
+      - uses: actions/cache@v3
+        with:
+          path: Library
+          key:
+            Library-${{ hashFiles('Assets/**', 'Packages/**', 'ProjectSettings/**') }}-${{ matrix.targetPlatform }}
+          restore-keys: |
+            Library-${{ matrix.targetPlatform }}
+            Library-
+      - uses: game-ci/unity-builder@v4
+        env:
+          UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
+          UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
+          UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
+        with:
+          targetPlatform: ${{ matrix.targetPlatform }}
+          allowDirtyBuild: true
+      - uses: actions/upload-artifact@v3
+        with:
+          name: Build
+          path: build
+
+  windows:
+    name: Build for ${{ matrix.targetPlatform }}
+    runs-on: windows-2022
+    strategy:
+      fail-fast: false
+      matrix:
+        targetPlatform:
+          - StandaloneWindows64
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          lfs: true
+      - uses: actions/cache@v3
+        with:
+          path: Library
+          key:
+            Library-${{ matrix.targetPlatform }}-${{ hashFiles('Assets/**', 'Packages/**', 'ProjectSettings/**') }}
+          restore-keys: |
+            Library-${{ matrix.targetPlatform }}
+            Library-
+      - uses: game-ci/unity-builder@v4
+        env:
+          UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
+          UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
+          UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
+        with:
+          targetPlatform: ${{ matrix.targetPlatform }}
+      - uses: actions/upload-artifact@v3
+        with:
+          name: Build
+          path: build
\ No newline at end of file
diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml
index 50e8269b..eac6918d 100644
--- a/.github/workflows/linter.yml
+++ b/.github/workflows/linter.yml
@@ -53,7 +53,7 @@ jobs:
           VALIDATE_ALL_CODEBASE: true
           DEFAULT_BRANCH: main
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-          FILTER_REGEX_EXCLUDE: (.*Assets/Plugins/Android/mainTemplate.gradle|.*Assets/Shared/Scripts/Data)
+          FILTER_REGEX_EXCLUDE: (.*Assets/Plugins/Android/mainTemplate.gradle|.*Assets/Shared/Scripts/Data|.*.github/workflows)
           VALIDATE_MARKDOWN: false
           VALIDATE_GITLEAKS: false
           VALIDATE_JSCPD: false
diff --git a/.gitignore b/.gitignore
index 30bea76f..86d38afe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -82,4 +82,8 @@ crashlytics-build.properties
 
 .env
 
-mono_crash.*
\ No newline at end of file
+mono_crash.*
+
+# Ignore temporaries from GameCI
+/[Aa]rtifacts/
+/[Cc]odeCoverage/
diff --git a/Assets/Core/Xsolla/Browser/XsollaWebBrowser.cs b/Assets/Core/Xsolla/Browser/XsollaWebBrowser.cs
index 00f012b5..1f78acd7 100644
--- a/Assets/Core/Xsolla/Browser/XsollaWebBrowser.cs
+++ b/Assets/Core/Xsolla/Browser/XsollaWebBrowser.cs
@@ -1,3 +1,5 @@
+using System;
+using System.Runtime.InteropServices;
 using UnityEngine;
 #if UNITY_WEBGL || UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
 #endif
diff --git a/Assets/Runner/Scripts/InputManager.cs b/Assets/Runner/Scripts/InputManager.cs
index ce0065f3..de8c7ff6 100644
--- a/Assets/Runner/Scripts/InputManager.cs
+++ b/Assets/Runner/Scripts/InputManager.cs
@@ -48,9 +48,9 @@ private void Update()
                 m_HasInput = false;
             }
 #else
-            if (Touch.activeTouches.Count > 0)
+            if (UnityEngine.InputSystem.EnhancedTouch.Touch.activeTouches.Count > 0)
             {
-                m_InputPosition = Touch.activeTouches[0].screenPosition;
+                m_InputPosition = UnityEngine.InputSystem.EnhancedTouch.Touch.activeTouches[0].screenPosition;
 
                 if (!m_HasInput)
                 {
diff --git a/Assets/Shared/Scripts/Domain/OrderbookManager.cs b/Assets/Shared/Scripts/Domain/OrderbookManager.cs
index 75a02377..a72fe9e9 100644
--- a/Assets/Shared/Scripts/Domain/OrderbookManager.cs
+++ b/Assets/Shared/Scripts/Domain/OrderbookManager.cs
@@ -143,7 +143,8 @@ private async UniTask<string> SignListing(PrepareListing200Response listingData)
                 throw new Exception("No valid listing to sign.");
 
             var messageJson = JsonConvert.SerializeObject(signableAction.Message, Formatting.Indented);
-            return await Passport.Instance.ZkEvmSignTypedDataV4(messageJson);
+            // return await Passport.Instance.ZkEvmSignTypedDataV4(messageJson);
+            return "";
         }
 
         /// <summary>
diff --git a/Assets/Shared/Scripts/Model/Config.cs b/Assets/Shared/Scripts/Model/Config.cs
index c7cb4ea1..d698a438 100644
--- a/Assets/Shared/Scripts/Model/Config.cs
+++ b/Assets/Shared/Scripts/Model/Config.cs
@@ -1,3 +1,6 @@
+using System;
+using UnityEngine;
+
 namespace HyperCasual.Runner
 {
     public static class Config
diff --git a/Assets/Shared/Services/ApiService.cs b/Assets/Shared/Services/ApiService.cs
index 53d6f525..88dfa569 100644
--- a/Assets/Shared/Services/ApiService.cs
+++ b/Assets/Shared/Services/ApiService.cs
@@ -3,6 +3,7 @@
 using Cysharp.Threading.Tasks;
 using HyperCasual.Runner;
 using UnityEngine;
+using UnityEngine.Networking;
 
 namespace Shared.Services
 {
diff --git a/Assets/Unity.Hypercasual.asmdef b/Assets/Unity.Hypercasual.asmdef
index 9584dea2..742703fe 100644
--- a/Assets/Unity.Hypercasual.asmdef
+++ b/Assets/Unity.Hypercasual.asmdef
@@ -14,7 +14,8 @@
         "GUID:95d173a3e67b39d40803000ed05b79f4",
         "GUID:95a2b1ff23bee4357a10943aba1b014d",
         "GUID:1d9728650655047939a351b40befbcdb",
-        "GUID:73ae64c02a64344a180df3fcbaab9f4b"
+        "GUID:73ae64c02a64344a180df3fcbaab9f4b",
+        "Immutable.Api"
     ],
     "includePlatforms": [],
     "excludePlatforms": [],
diff --git a/Packages/manifest.json b/Packages/manifest.json
index ae8ad321..1aa76801 100644
--- a/Packages/manifest.json
+++ b/Packages/manifest.json
@@ -3,7 +3,7 @@
     "com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.3.3",
     "com.immutable.marketplace": "https://github.com/immutable/unity-immutable-sdk.git?path=/src/Packages/Marketplace",
     "com.immutable.orderbook": "https://github.com/immutable/unity-immutable-sdk.git?path=/src/Packages/Orderbook",
-    "com.immutable.passport": "file:/Users/natalie/Development/unity-immutable-sdk/src/Packages/Passport",
+    "com.immutable.passport": "https://github.com/immutable/unity-immutable-sdk.git?path=/src/Packages/Passport",
     "com.nobi.roundedcorners": "https://github.com/kirevdokimov/Unity-UI-Rounded-Corners.git",
     "com.unity.collab-proxy": "2.0.4",
     "com.unity.ide.rider": "3.0.31",
diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json
index ec45d027..853d0255 100644
--- a/Packages/packages-lock.json
+++ b/Packages/packages-lock.json
@@ -26,13 +26,14 @@
       "hash": "e8bc73fce00813ce0abc7ce442e1d8b8bdf03779"
     },
     "com.immutable.passport": {
-      "version": "file:/Users/natalie/Development/unity-immutable-sdk/src/Packages/Passport",
+      "version": "https://github.com/immutable/unity-immutable-sdk.git?path=/src/Packages/Passport",
       "depth": 0,
-      "source": "local",
+      "source": "git",
       "dependencies": {
         "com.unity.nuget.newtonsoft-json": "3.2.0",
         "com.cysharp.unitask": "2.3.3"
-      }
+      },
+      "hash": "e8bc73fce00813ce0abc7ce442e1d8b8bdf03779"
     },
     "com.nobi.roundedcorners": {
       "version": "https://github.com/kirevdokimov/Unity-UI-Rounded-Corners.git",
diff --git a/contracts/hardhat.config.ts b/contracts/hardhat.config.ts
index cc5ed211..a0f6cc5f 100644
--- a/contracts/hardhat.config.ts
+++ b/contracts/hardhat.config.ts
@@ -56,7 +56,7 @@ const config: HardhatUserConfig = {
           apiURL: 'https://explorer.dev.immutable.com/api',
           browserURL: 'https://explorer.dev.immutable.com',
         },
-      }
+      },
     ],
   },
   sourcify: {
diff --git a/contracts/scripts/deploy.ts b/contracts/scripts/deploy.ts
index a144871a..692d9951 100644
--- a/contracts/scripts/deploy.ts
+++ b/contracts/scripts/deploy.ts
@@ -16,7 +16,7 @@ async function main() {
   // Deploy the contract to the zkEVM network
   const packContract = await packContractFactory.deploy(
     '0x328766302e7617d0de5901f8da139dca49f3ec75', // Immutable Runner Token contract address
-    '0x5A3461514af018c19A6F887d14840B05fED4c5b8' // Immutable Operator Allowlist
+    '0x5A3461514af018c19A6F887d14840B05fED4c5b8', // Immutable Operator Allowlist
   );
 
   console.log('Contract deployed to:', await packContract.getAddress());
diff --git a/contracts/test/RunnerPack.test.ts b/contracts/test/RunnerPack.test.ts
index ac4f3e7a..9f226835 100644
--- a/contracts/test/RunnerPack.test.ts
+++ b/contracts/test/RunnerPack.test.ts
@@ -2,7 +2,6 @@ import { ethers } from 'hardhat';
 import { expect } from 'chai';
 // eslint-disable-next-line import/extensions
 import { RunnerToken, OperatorAllowlist__factory, RunnerToken__factory, ImmutableERC721, ImmutableERC721__factory, RunnerPack, RunnerPack__factory } from '../typechain-types';
-import { token } from '../typechain-types/@openzeppelin/contracts';
 
 describe('RunnerShield', function () {
   let tokenContract: RunnerToken;
@@ -15,7 +14,7 @@ describe('RunnerShield', function () {
 
     // deploy OperatorAllowlist contract
     const OperatorAllowlist = await ethers.getContractFactory(
-      'OperatorAllowlist'
+      'OperatorAllowlist',
     ) as unknown as OperatorAllowlist__factory;
     const operatorAllowlist = await OperatorAllowlist.deploy(owner.address);
     const operatorAllowlistAddress = await operatorAllowlist.getAddress();
@@ -58,13 +57,13 @@ describe('RunnerShield', function () {
     const shieldId = await contract.GALACTIC_SHIELD();
     const clearId = await contract.CLEAR_SKIES();
 
-    await contract.connect(owner).safeMint(recipient.address, shieldId.toString(), 5, "0x");
+    await contract.connect(owner).safeMint(recipient.address, shieldId.toString(), 5, '0x');
     expect(await contract.balanceOf(recipient.address, shieldId)).to.equal(5);
 
-    await contract.connect(owner).safeMint(recipient.address, shieldId.toString(), 5, "0x");
+    await contract.connect(owner).safeMint(recipient.address, shieldId.toString(), 5, '0x');
     expect(await contract.balanceOf(recipient.address, shieldId)).to.equal(10);
 
-    await contract.connect(owner).safeMint(recipient.address, clearId.toString(), 8, "0x");
+    await contract.connect(owner).safeMint(recipient.address, clearId.toString(), 8, '0x');
     expect(await contract.balanceOf(recipient.address, clearId)).to.equal(8);
   });
 
@@ -76,7 +75,7 @@ describe('RunnerShield', function () {
     const shieldId = await contract.GALACTIC_SHIELD();
 
     await expect(
-      contract.connect(acc1).safeMint(acc1.address, shieldId.toString(), 5, "0x")
+      contract.connect(acc1).safeMint(acc1.address, shieldId.toString(), 5, '0x'),
     ).to.be.revertedWith(
       `AccessControl: account ${acc1.address.toLowerCase()} is missing role ${minterRole}`,
     );
@@ -87,7 +86,7 @@ describe('RunnerShield', function () {
 
     const shieldId = await contract.GALACTIC_SHIELD();
 
-    await contract.connect(owner).safeMint(recipient.address, shieldId.toString(), 2, "0x");
+    await contract.connect(owner).safeMint(recipient.address, shieldId.toString(), 2, '0x');
     expect(await contract.balanceOf(recipient.address, shieldId)).to.equal(2);
 
     await contract.connect(recipient).burn(recipient.address, shieldId, 1);
@@ -99,14 +98,14 @@ describe('RunnerShield', function () {
 
     const shieldId = await contract.GALACTIC_SHIELD();
 
-    await contract.connect(owner).safeMint(recipient.address, shieldId.toString(), 1, "0x");
+    await contract.connect(owner).safeMint(recipient.address, shieldId.toString(), 1, '0x');
     expect(await contract.balanceOf(recipient.address, shieldId)).to.equal(1);
 
     await contract.connect(recipient).burn(recipient.address, shieldId, 1);
     expect(await contract.balanceOf(recipient.address, shieldId)).to.equal(0);
 
     await expect(
-      contract.connect(recipient).burn(recipient.address, shieldId, 1)
+      contract.connect(recipient).burn(recipient.address, shieldId, 1),
     ).to.be.revertedWith('ERC1155: burn amount exceeds totalSupply');
   });
 
@@ -115,11 +114,11 @@ describe('RunnerShield', function () {
 
     const shieldId = await contract.GALACTIC_SHIELD();
 
-    await contract.connect(owner).safeMint(recipient.address, shieldId.toString(), 2, "0x");
+    await contract.connect(owner).safeMint(recipient.address, shieldId.toString(), 2, '0x');
     expect(await contract.balanceOf(recipient.address, shieldId)).to.equal(2);
 
     await expect(
-      contract.connect(acc1).burn(recipient.address, shieldId, 1)
+      contract.connect(acc1).burn(recipient.address, shieldId, 1),
     ).to.be.revertedWith('ERC1155: caller is not token owner or approved');
   });
 
@@ -161,8 +160,6 @@ describe('RunnerShield', function () {
   it('Without approval account cannot buy galactic shield pack', async function () {
     const [owner, recipient] = await ethers.getSigners();
 
-    const shieldId = await contract.GALACTIC_SHIELD();
-
     // Mint ten tokens to recipient
     const tenTokens = 10n * (10n ** await tokenContract.decimals());
     await tokenContract.connect(owner).mint(recipient.address, tenTokens);
@@ -211,8 +208,6 @@ describe('RunnerShield', function () {
   it('Without approval account cannot buy clear skies pack', async function () {
     const [owner, recipient] = await ethers.getSigners();
 
-    const clearSkiesId = await contract.CLEAR_SKIES();
-
     // Mint eight tokens to recipient
     const eightTokens = 8n * (10n ** await tokenContract.decimals());
     await tokenContract.connect(owner).mint(recipient.address, eightTokens);
@@ -263,9 +258,6 @@ describe('RunnerShield', function () {
   it('Without approval account cannot buy navigators combo pack', async function () {
     const [owner, recipient] = await ethers.getSigners();
 
-    const shieldId = await contract.GALACTIC_SHIELD();
-    const clearSkiesId = await contract.CLEAR_SKIES();
-
     // Mint nine tokens to recipient
     const nineTokens = 9n * (10n ** await tokenContract.decimals());
     await tokenContract.connect(owner).mint(recipient.address, nineTokens);
diff --git a/contracts/yarn.lock b/contracts/yarn.lock
index aa40b5b9..1de862e9 100644
--- a/contracts/yarn.lock
+++ b/contracts/yarn.lock
@@ -4589,9 +4589,9 @@ seaport-types@^0.0.1:
   resolved "https://registry.yarnpkg.com/seaport-types/-/seaport-types-0.0.1.tgz#e2a32fe8641853d7dadb1b0232d911d88ccc3f1a"
   integrity sha512-m7MLa7sq3YPwojxXiVvoX1PM9iNVtQIn7AdEtBnKTwgxPfGRWUlbs/oMgetpjT/ZYTmv3X5/BghOcstWYvKqRA==
 
-"seaport@git+https://github.com/immutable/seaport.git#1.5.0+im.1.3":
+"seaport@https://github.com/immutable/seaport.git#1.5.0+im.1.3":
   version "1.5.0"
-  resolved "git+https://github.com/immutable/seaport.git#ae061dc008105dd8d05937df9ad9a676f878cbf9"
+  resolved "https://github.com/immutable/seaport.git#ae061dc008105dd8d05937df9ad9a676f878cbf9"
   dependencies:
     "@nomicfoundation/hardhat-network-helpers" "^1.0.7"
     "@openzeppelin/contracts" "^4.9.2"
diff --git a/mint-backend/src/index.ts b/mint-backend/src/index.ts
index 24488ffc..d4db5b8d 100644
--- a/mint-backend/src/index.ts
+++ b/mint-backend/src/index.ts
@@ -271,8 +271,8 @@ router.get('/packs', async (req: Request, res: Response) => {
 router.post('/pack/checkApprovalRequired', async (req: Request, res: Response) => {
   try {
     if (tokenContractAddress && packContractAddress && privateKey) {
-      let address: string = req.body.address ?? null;
-      let amount: string = req.body.amount ?? null;
+      const address: string = req.body.address ?? null;
+      const amount: string = req.body.amount ?? null;
 
       // Call allowance
       const abi = [
@@ -287,8 +287,8 @@ router.post('/pack/checkApprovalRequired', async (req: Request, res: Response) =
 
       if (BigNumber.from(approvedAmountDecimal).lt(BigNumber.from(amount))) {
         console.log('The approved amount is less than the requested amount.');
-        let iface = new utils.Interface(abi);
-        let encodedData = iface.encodeFunctionData("approve", [packContractAddress, amount]);
+        const iface = new utils.Interface(abi);
+        const encodedData = iface.encodeFunctionData("approve", [packContractAddress, amount]);
         console.log(`encodedData: ${encodedData}`);
 
         return res.status(200).json({