diff --git a/README.md b/README.md
index af767294..dd875bf4 100644
--- a/README.md
+++ b/README.md
@@ -49,10 +49,10 @@ file needs to define these variables and dependencies, too. A working template w
 folder's `build.gradle` file.
 - Don't forget to `include ':ipv8'` into your own `settings.gradle`,
 as well as the module that you're going to use, presumably `ipv8-android` or `ipv8-jvm`.
-- This repository currently uses Gradle version `6.1.1`. Ensure that your `gradle-wrapper.properties` uses the same version.
-- This repository currently uses Java version `1.8`. Ensure that your Gradle builds with this, too.
-  - By default, Gradle looks at the `JAVA_HOME` variable, which might not point to `1.8`.
-- This repository currently uses Kotlin version `1.4.21`. Ensure that your Gradle builds with this Kotlin version.
+- This repository currently uses Gradle version `8.8.0`. Ensure that your `gradle-wrapper.properties` uses the same version.
+- This repository currently uses Java version `17`. Ensure that your Gradle builds with this, too.
+  - By default, Gradle looks at the `JAVA_HOME` variable, which might not point to `17`.
+- This repository currently uses Kotlin version `2.1.10`. Ensure that your Gradle builds with this Kotlin version.
 
 For an example of a project that uses this repository, refer to
 [the Trustchain app](https://github.com/Tribler/trustchain-superapp/).
diff --git a/build.gradle b/build.gradle
index 708ffe75..16a15c11 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,16 +1,16 @@
 // Top-level build file where you can add configuration options common to all sub-projects/modules.
 
 buildscript {
-    ext.kotlin_version = '1.9.22'
-    ext.coroutines_version = '1.6.4'
-    ext.ktlint_version = '1.1.1'
-    ext.ktlint_gradle_version = '12.1.0'
+    ext.kotlin_version = '2.1.10'
+    ext.coroutines_version = '1.10.1'
+    ext.ktlint_version = '1.5.0'
+    ext.ktlint_gradle_version = '12.1.2'
     ext.sqldelight_version = '2.0.1'
-    ext.nav_version = '2.5.3'
-    ext.fragment_version = '1.5.4'
-    ext.lifecycle_version = '2.5.1'
+    ext.nav_version = '2.8.6'
+    ext.fragment_version = '1.8.5'
+    ext.lifecycle_version = '2.8.7'
     ext.dokka_version = '0.10.1'
-    ext.mockk_version = '1.13.9'
+    ext.mockk_version = '1.13.16'
     repositories {
         google()
         mavenCentral()
@@ -18,7 +18,7 @@ buildscript {
         maven { url 'https://jitpack.io' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:8.2.0'
+        classpath 'com.android.tools.build:gradle:8.8.0'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
         classpath "org.jlleitschuh.gradle:ktlint-gradle:$ktlint_gradle_version"
         classpath "app.cash.sqldelight:gradle-plugin:$sqldelight_version"
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 2f10fb20..b2933658 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
 #Tue Jan 12 11:23:56 CET 2021
 distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
 distributionPath=wrapper/dists
 zipStorePath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
diff --git a/ipv8/src/main/java/nl/tudelft/ipv8/attestation/identity/Metadata.kt b/ipv8/src/main/java/nl/tudelft/ipv8/attestation/identity/Metadata.kt
index b3d1b2fe..bce6f843 100644
--- a/ipv8/src/main/java/nl/tudelft/ipv8/attestation/identity/Metadata.kt
+++ b/ipv8/src/main/java/nl/tudelft/ipv8/attestation/identity/Metadata.kt
@@ -22,7 +22,6 @@ class Metadata(
         return arrayOf(this.tokenPointer, this.signature, this.serializedJSONObject)
     }
 
-    @ExperimentalStdlibApi
     override fun toString(): String {
         return "Metadata(${this.tokenPointer.toHex()},\n${this.serializedJSONObject.toString(Charsets.UTF_8)}"
     }
diff --git a/ipv8/src/main/java/nl/tudelft/ipv8/util/HexUtils.kt b/ipv8/src/main/java/nl/tudelft/ipv8/util/HexUtils.kt
index 82f5fcb5..962d0c7f 100644
--- a/ipv8/src/main/java/nl/tudelft/ipv8/util/HexUtils.kt
+++ b/ipv8/src/main/java/nl/tudelft/ipv8/util/HexUtils.kt
@@ -30,10 +30,8 @@ fun String.hexToBytes(): ByteArray {
     val result = ByteArray(length / 2)
 
     for (i in 0 until length step 2) {
-        @Suppress("DEPRECATION")
-        val firstIndex = HEX_CHARS.indexOf(this[i].toLowerCase())
-        @Suppress("DEPRECATION")
-        val secondIndex = HEX_CHARS.indexOf(this[i + 1].toLowerCase())
+        val firstIndex = HEX_CHARS.indexOf(this[i].lowercaseChar())
+        val secondIndex = HEX_CHARS.indexOf(this[i + 1].lowercaseChar())
 
         val octet = firstIndex.shl(4).or(secondIndex)
         result[i.shr(1)] = octet.toByte()
diff --git a/ipv8/src/test/java/nl/tudelft/ipv8/BaseCommunityTest.kt b/ipv8/src/test/java/nl/tudelft/ipv8/BaseCommunityTest.kt
index 70e84203..a0b4ab1a 100644
--- a/ipv8/src/test/java/nl/tudelft/ipv8/BaseCommunityTest.kt
+++ b/ipv8/src/test/java/nl/tudelft/ipv8/BaseCommunityTest.kt
@@ -8,7 +8,7 @@ import io.mockk.mockk
 import io.mockk.spyk
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestCoroutineDispatcher
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.resetMain
 import kotlinx.coroutines.test.setMain
 import nl.tudelft.ipv8.keyvault.LibNaClSK
@@ -22,7 +22,7 @@ private val lazySodium = LazySodiumJava(SodiumJava())
 
 @OptIn(ExperimentalCoroutinesApi::class)
 abstract class BaseCommunityTest {
-    private val testDispatcher = TestCoroutineDispatcher()
+    private val testDispatcher = UnconfinedTestDispatcher()
 
     @Before
     fun setUp() {
@@ -32,7 +32,6 @@ abstract class BaseCommunityTest {
     @After
     fun tearDown() {
         Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher
-        testDispatcher.cleanupTestCoroutines()
     }
 
     protected fun getPrivateKey(): PrivateKey {
diff --git a/ipv8/src/test/java/nl/tudelft/ipv8/attestation/trustchain/TrustChainCrawlerTest.kt b/ipv8/src/test/java/nl/tudelft/ipv8/attestation/trustchain/TrustChainCrawlerTest.kt
index 9bf5037b..6a1b33c3 100644
--- a/ipv8/src/test/java/nl/tudelft/ipv8/attestation/trustchain/TrustChainCrawlerTest.kt
+++ b/ipv8/src/test/java/nl/tudelft/ipv8/attestation/trustchain/TrustChainCrawlerTest.kt
@@ -6,6 +6,7 @@ import io.mockk.coEvery
 import io.mockk.spyk
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.test.runTest
 import nl.tudelft.ipv8.BaseCommunityTest
 import nl.tudelft.ipv8.Peer
 import nl.tudelft.ipv8.attestation.trustchain.store.TrustChainSQLiteStore
@@ -39,7 +40,7 @@ class TrustChainCrawlerTest : BaseCommunityTest() {
     @Suppress("DEPRECATION") // TODO: rewrite usage of coroutines in testing.
     @Test
     fun crawlChain_noop() =
-        runBlockingTest {
+        runTest {
             val crawler = TrustChainCrawler()
             val trustChainCommunity = spyk(getCommunity())
             crawler.trustChainCommunity = trustChainCommunity
@@ -71,7 +72,7 @@ class TrustChainCrawlerTest : BaseCommunityTest() {
     @Suppress("DEPRECATION") // TODO: rewrite usage of coroutines in testing.
     @Test
     fun crawlChain_singleBlock() =
-        runBlockingTest {
+        runTest {
             val crawler = TrustChainCrawler()
             val trustChainCommunity = spyk(getCommunity())
             crawler.trustChainCommunity = trustChainCommunity
@@ -124,7 +125,7 @@ class TrustChainCrawlerTest : BaseCommunityTest() {
     @Suppress("DEPRECATION") // TODO: rewrite usage of coroutines in testing.
     @Test
     fun crawlChain_fillGap() =
-        runBlockingTest {
+        runTest {
             val crawler = TrustChainCrawler()
             val trustChainCommunity = spyk(getCommunity())
             crawler.trustChainCommunity = trustChainCommunity
diff --git a/ipv8/src/test/java/nl/tudelft/ipv8/messaging/tftp/TFTPEndpointTest.kt b/ipv8/src/test/java/nl/tudelft/ipv8/messaging/tftp/TFTPEndpointTest.kt
index f145b7a2..7039cf89 100644
--- a/ipv8/src/test/java/nl/tudelft/ipv8/messaging/tftp/TFTPEndpointTest.kt
+++ b/ipv8/src/test/java/nl/tudelft/ipv8/messaging/tftp/TFTPEndpointTest.kt
@@ -3,6 +3,7 @@ package nl.tudelft.ipv8.messaging.tftp
 import io.mockk.*
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.test.runTest
 import nl.tudelft.ipv8.IPv4Address
 import org.apache.commons.net.tftp.TFTP
 import org.apache.commons.net.tftp.TFTPAckPacket
@@ -17,7 +18,7 @@ import java.net.InetAddress
 class TFTPEndpointTest {
     @Suppress("DEPRECATION") // TODO: rewrite usage of coroutines in testing.
     @Test
-    fun send() = runBlockingTest {
+    fun send() = runTest {
         val tftpClient = spyk<TFTPClient>()
         val socket = mockk<DatagramSocket>(relaxed = true)
         val tftpEndpoint = TFTPEndpoint(tftpClient)
@@ -42,7 +43,7 @@ class TFTPEndpointTest {
 
     @Suppress("DEPRECATION") // TODO: rewrite usage of coroutines in testing.
     /* @Test */
-    fun onPacket_forServer() = runBlockingTest {
+    fun onPacket_forServer() = runTest {
         val tftpClient = mockk<TFTPClient>(relaxed = true)
         val socket = mockk<DatagramSocket>(relaxed = true)
         val tftpEndpoint = TFTPEndpoint(tftpClient)
@@ -73,7 +74,7 @@ class TFTPEndpointTest {
 
     @Suppress("DEPRECATION") // TODO: rewrite usage of coroutines in testing.
     @Test
-    fun onPacket_forClient() = runBlockingTest {
+    fun onPacket_forClient() = runTest {
         val tftpClient = TFTPClient()
         val socket = mockk<DatagramSocket>(relaxed = true)
         val tftpEndpoint = TFTPEndpoint(tftpClient)
diff --git a/ipv8/src/test/java/nl/tudelft/ipv8/util/HexUtilsTest.kt b/ipv8/src/test/java/nl/tudelft/ipv8/util/HexUtilsTest.kt
index 6ec913d6..76d7c5b8 100644
--- a/ipv8/src/test/java/nl/tudelft/ipv8/util/HexUtilsTest.kt
+++ b/ipv8/src/test/java/nl/tudelft/ipv8/util/HexUtilsTest.kt
@@ -3,6 +3,7 @@ package nl.tudelft.ipv8.util
 import org.junit.Assert
 import org.junit.Test
 import java.lang.IllegalArgumentException
+import java.util.*
 
 class HexUtilsTest {
     @Test
@@ -32,8 +33,7 @@ class HexUtilsTest {
     fun hexToBytesToHex() {
         val txid = "d19306e0"
         Assert.assertEquals(txid, txid.hexToBytes().toHex())
-        @Suppress("DEPRECATION")
-        Assert.assertEquals(txid, txid.toUpperCase().hexToBytes().toHex())
+        Assert.assertEquals(txid, txid.uppercase(Locale.getDefault()).hexToBytes().toHex())
     }
 
     @Test(expected = IllegalArgumentException::class)