diff --git a/package-lock.json b/package-lock.json
index 0c1b1a61523..f5d4ceb3f1c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -21484,8 +21484,7 @@
         "ethereum-cryptography": "^1.1.2",
         "level": "^8.0.0",
         "memory-level": "^1.0.0",
-        "readable-stream": "^3.6.0",
-        "semaphore-async-await": "^1.5.1"
+        "readable-stream": "^3.6.0"
       },
       "devDependencies": {
         "@types/benchmark": "^1.0.33",
@@ -22913,7 +22912,6 @@
         "memory-level": "^1.0.0",
         "micro-bmark": "0.2.0",
         "readable-stream": "^3.6.0",
-        "semaphore-async-await": "^1.5.1",
         "web3": "^1.7.5"
       }
     },
diff --git a/packages/trie/package.json b/packages/trie/package.json
index 1fe35c9d8c6..47097ea139f 100644
--- a/packages/trie/package.json
+++ b/packages/trie/package.json
@@ -51,8 +51,7 @@
     "ethereum-cryptography": "^1.1.2",
     "level": "^8.0.0",
     "memory-level": "^1.0.0",
-    "readable-stream": "^3.6.0",
-    "semaphore-async-await": "^1.5.1"
+    "readable-stream": "^3.6.0"
   },
   "devDependencies": {
     "0x": "^4.9.1",
diff --git a/packages/trie/src/trie/trie.ts b/packages/trie/src/trie/trie.ts
index 8041b4c3280..3564e84f245 100644
--- a/packages/trie/src/trie/trie.ts
+++ b/packages/trie/src/trie/trie.ts
@@ -1,12 +1,12 @@
 import { RLP_EMPTY_STRING, isFalsy, isTruthy } from '@ethereumjs/util'
 import { keccak256 } from 'ethereum-cryptography/keccak'
-import Semaphore from 'semaphore-async-await'
 
 import { LevelDB } from '../db'
 import { verifyRangeProof } from '../proof/range'
 import { ROOT_DB_KEY } from '../types'
 import { bufferToNibbles, doKeysMatch, matchingNibbleLength } from '../util/nibbles'
 import { TrieReadStream as ReadStream } from '../util/readStream'
+import { Semaphore } from '../util/semaphore'
 import { WalkController } from '../util/walkController'
 
 import { BranchNode, ExtensionNode, LeafNode, decodeNode, decodeRawNode, isRawNode } from './node'
diff --git a/packages/trie/src/util/semaphore.ts b/packages/trie/src/util/semaphore.ts
new file mode 100644
index 00000000000..680ae31d910
--- /dev/null
+++ b/packages/trie/src/util/semaphore.ts
@@ -0,0 +1,52 @@
+// Based on https://github.com/jsoendermann/semaphore-async-await/blob/master/src/Semaphore.ts
+export class Semaphore {
+  private permits: number
+  private promiseResolverQueue: Array<(v: boolean) => void> = []
+
+  /**
+   * Creates a semaphore.
+   * @param permits  The number of permits, i.e. strands of execution being allowed
+   * to run in parallel.
+   * This number can be initialized with a negative integer.
+   */
+  constructor(permits: number) {
+    this.permits = permits
+  }
+
+  /**
+   * Returns a promise used to wait for a permit to become available. This method should be awaited on.
+   * @returns  A promise that gets resolved when execution is allowed to proceed.
+   */
+  public async wait(): Promise<boolean> {
+    if (this.permits > 0) {
+      this.permits -= 1
+      return Promise.resolve(true)
+    }
+
+    // If there is no permit available, we return a promise that resolves once the semaphore gets
+    // signaled enough times that permits is equal to one.
+    return new Promise<boolean>((resolver) => this.promiseResolverQueue.push(resolver))
+  }
+
+  /**
+   * Increases the number of permits by one. If there are other functions waiting, one of them will
+   * continue to execute in a future iteration of the event loop.
+   */
+  public signal(): void {
+    this.permits += 1
+
+    if (this.permits > 1 && this.promiseResolverQueue.length > 0) {
+      // eslint-disable-next-line no-console
+      console.warn('Semaphore.permits should never be > 0 when there is someone waiting.')
+    } else if (this.permits === 1 && this.promiseResolverQueue.length > 0) {
+      // If there is someone else waiting, immediately consume the permit that was released
+      // at the beginning of this function and let the waiting function resume.
+      this.permits -= 1
+
+      const nextResolver = this.promiseResolverQueue.shift()
+      if (nextResolver) {
+        nextResolver(true)
+      }
+    }
+  }
+}
diff --git a/packages/trie/test/util/semaphore.spec.ts b/packages/trie/test/util/semaphore.spec.ts
new file mode 100644
index 00000000000..818141dbb00
--- /dev/null
+++ b/packages/trie/test/util/semaphore.spec.ts
@@ -0,0 +1,29 @@
+// Based on https://github.com/jsoendermann/semaphore-async-await/blob/master/__tests__/Semaphore.spec.ts
+import * as tape from 'tape'
+
+import { Semaphore } from '../../src/util/semaphore'
+
+const wait = (ms: number) => new Promise((r) => setTimeout(r, ms))
+
+tape('Semaphore', (t) => {
+  t.test('should lock', async (st) => {
+    let global = 0
+    const lock = new Semaphore(1)
+
+    const f = async () => {
+      await lock.wait()
+      const local = global
+      await wait(500)
+      global = local + 1
+      lock.signal()
+    }
+
+    void f()
+    void f()
+    await wait(1500)
+
+    st.equal(global, 2)
+    st.end()
+  })
+  t.end()
+})