diff --git a/apps/token-server/config.ts b/apps/token-server/config.ts
--- a/apps/token-server/config.ts
+++ b/apps/token-server/config.ts
@@ -13,8 +13,18 @@
     handler: RateLimitExceededEventHandler;
 }
 
+interface DbCollections {
+    blacklist: { name: string };
+}
+
+interface DatabaseConfig {
+    name: string;
+    collections: DbCollections;
+}
+
 interface TokenServerConfig {
     port: Number;
+    db: DatabaseConfig;
     chronikUrls: string[];
     eligibilityResetSeconds: number;
     rewardsTokenId: string;
@@ -33,6 +43,10 @@
 
 const config: TokenServerConfig = {
     port: 3333,
+    db: {
+        name: 'tokenServerDb',
+        collections: { blacklist: { name: 'blacklist' } },
+    },
     chronikUrls: [
         'https://chronik-native1.fabien.cash',
         'https://chronik-native2.fabien.cash',
diff --git a/apps/token-server/index.ts b/apps/token-server/index.ts
--- a/apps/token-server/index.ts
+++ b/apps/token-server/index.ts
@@ -11,55 +11,93 @@
 import fs from 'fs';
 import { Ecc, initWasm } from 'ecash-lib';
 import { rateLimit } from 'express-rate-limit';
+import { MongoClient } from 'mongodb';
+import { initializeDb } from './src/db';
 
 // Connect to available in-node chronik servers
 const chronik = new ChronikClient(config.chronikUrls);
 
+// Connect to database
+// Connection URL (default)
+const MONGODB_URL = `mongodb://${secrets.prod.db.username}:${secrets.prod.db.password}@${secrets.prod.db.containerName}:${secrets.prod.db.port}`;
+const client = new MongoClient(MONGODB_URL);
+// Check if database exists
+
 // Initialize websocket connection and log incoming blocks
 initWasm().then(
     () => {
-        const ecc = new Ecc();
-        // Initialize telegramBot
-        const telegramBot = initializeTelegramBot(
-            secrets.prod.botId,
-            secrets.prod.approvedMods,
-            fs,
-        );
+        initializeDb(client).then(
+            db => {
+                const ecc = new Ecc();
+                // Initialize telegramBot
+                const telegramBot = initializeTelegramBot(
+                    secrets.prod.botId,
+                    secrets.prod.approvedMods,
+                    fs,
+                );
 
-        // Start the express app to expose API endpoints
-        const server = startExpressServer(
-            config.port,
-            chronik,
-            telegramBot,
-            fs,
-            ecc,
-            rateLimit(config.limiter),
-            rateLimit(config.tokenLimiter),
-        );
-        console.log(`Express server started on port ${config.port}`);
+                // Start the express app to expose API endpoints
+                const server = startExpressServer(
+                    config.port,
+                    db,
+                    chronik,
+                    telegramBot,
+                    fs,
+                    ecc,
+                    rateLimit(config.limiter),
+                    rateLimit(config.tokenLimiter),
+                );
+                console.log(`Express server started on port ${config.port}`);
 
-        // Gracefully shut down on app termination
-        process.on('SIGTERM', () => {
-            // kill <pid> from terminal
-            server.close();
-            console.log('token-server shut down by SIGTERM');
-            // Shut down the telegram bot
-            telegramBot.stopPolling();
-            process.exit(0);
-        });
+                // Gracefully shut down on app termination
+                process.on('SIGTERM', () => {
+                    // kill <pid> from terminal
+                    server.close();
+                    console.log('token-server shut down by SIGTERM');
+                    // Shut down the telegram bot
+                    telegramBot.stopPolling();
 
-        process.on('SIGINT', () => {
-            // ctrl + c in nodejs
-            server.close();
-            console.log('token-server shut down by ctrl+c');
-            // Shut down the telegram bot
-            telegramBot.stopPolling();
-            process.exit(0);
-        });
+                    // Shut down the database
+                    client.close().then(() => {
+                        console.log('MongoDB connection closed');
+                        // Shut down token-server in non-error condition
+                        process.exit(0);
+                    });
+                });
+
+                process.on('SIGINT', () => {
+                    // ctrl + c in nodejs
+                    server.close();
+                    console.log('token-server shut down by ctrl+c');
+                    // Shut down the telegram bot
+                    telegramBot.stopPolling();
+
+                    // Shut down the database
+                    client.close().then(() => {
+                        console.log('MongoDB connection closed');
+                        // Shut down token-server in non-error condition
+                        process.exit(0);
+                    });
+                });
+            },
+            err => {
+                console.log(`Error initializing database`, err);
+                // Shut down the database
+                client.close().then(() => {
+                    console.log('MongoDB connection closed');
+                    // Shut down token-server in error condition
+                    process.exit(1);
+                });
+            },
+        );
     },
     err => {
-        console.log(`Error initializing websocket in token-server`, err);
-        // Shut down in error condition
-        process.exit(1);
+        console.log(`Error initializing webassembly in token-server`, err);
+        // Shut down the database
+        client.close().then(() => {
+            console.log('MongoDB connection closed');
+            // Shut down token-server in error condition
+            process.exit(1);
+        });
     },
 );
diff --git a/apps/token-server/package-lock.json b/apps/token-server/package-lock.json
--- a/apps/token-server/package-lock.json
+++ b/apps/token-server/package-lock.json
@@ -22,6 +22,7 @@
                 "express": "^4.18.3",
                 "express-rate-limit": "^7.4.0",
                 "helmet": "^7.1.0",
+                "mongodb": "^6.10.0",
                 "multer": "^1.4.5-lts.1",
                 "node-telegram-bot-api": "^0.65.1",
                 "sharp": "^0.33.2",
@@ -31,6 +32,7 @@
                 "@types/cors": "^2.8.17",
                 "@types/express": "^4.17.21",
                 "@types/mocha": "^10.0.6",
+                "@types/mongodb": "^4.0.6",
                 "@types/multer": "^1.4.11",
                 "@types/node": "^20.11.29",
                 "@types/node-telegram-bot-api": "^0.64.6",
@@ -42,6 +44,7 @@
                 "mocha": "^10.3.0",
                 "mocha-junit-reporter": "^2.2.1",
                 "mocha-suppress-logs": "^0.5.1",
+                "mongodb-memory-server": "^10.1.2",
                 "nyc": "^15.1.0",
                 "supertest": "^6.3.4",
                 "ts-node": "^10.9.2",
@@ -1394,6 +1397,15 @@
                 "@jridgewell/sourcemap-codec": "^1.4.14"
             }
         },
+        "node_modules/@mongodb-js/saslprep": {
+            "version": "1.1.9",
+            "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz",
+            "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==",
+            "license": "MIT",
+            "dependencies": {
+                "sparse-bitfield": "^3.0.3"
+            }
+        },
         "node_modules/@noble/hashes": {
             "version": "1.3.3",
             "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz",
@@ -1560,6 +1572,16 @@
             "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==",
             "dev": true
         },
+        "node_modules/@types/mongodb": {
+            "version": "4.0.6",
+            "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-4.0.6.tgz",
+            "integrity": "sha512-XTbn1Z1j7fHzC1Vkd9LYO48lO2C581r+oRCi/KNzcTHIri7hEaya8r9vxoHJiKr+oeUWVK69+9xr84Mp+aReaw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "mongodb": "*"
+            }
+        },
         "node_modules/@types/multer": {
             "version": "1.4.11",
             "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.11.tgz",
@@ -1674,6 +1696,21 @@
             "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
             "dev": true
         },
+        "node_modules/@types/webidl-conversions": {
+            "version": "7.0.3",
+            "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
+            "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==",
+            "license": "MIT"
+        },
+        "node_modules/@types/whatwg-url": {
+            "version": "11.0.5",
+            "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz",
+            "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==",
+            "license": "MIT",
+            "dependencies": {
+                "@types/webidl-conversions": "*"
+            }
+        },
         "node_modules/@ungap/structured-clone": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
@@ -1722,6 +1759,19 @@
                 "node": ">=0.4.0"
             }
         },
+        "node_modules/agent-base": {
+            "version": "7.1.1",
+            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
+            "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "debug": "^4.3.4"
+            },
+            "engines": {
+                "node": ">= 14"
+            }
+        },
         "node_modules/aggregate-error": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
@@ -1906,6 +1956,16 @@
                 "node": ">=0.8"
             }
         },
+        "node_modules/async-mutex": {
+            "version": "0.5.0",
+            "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz",
+            "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "tslib": "^2.4.0"
+            }
+        },
         "node_modules/asynckit": {
             "version": "0.4.0",
             "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -1987,12 +2047,27 @@
                 "node": ">=4"
             }
         },
+        "node_modules/b4a": {
+            "version": "1.6.7",
+            "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz",
+            "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==",
+            "dev": true,
+            "license": "Apache-2.0"
+        },
         "node_modules/balanced-match": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
             "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
             "dev": true
         },
+        "node_modules/bare-events": {
+            "version": "2.5.0",
+            "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz",
+            "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==",
+            "dev": true,
+            "license": "Apache-2.0",
+            "optional": true
+        },
         "node_modules/bcrypt-pbkdf": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
@@ -2180,6 +2255,25 @@
                 "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
             }
         },
+        "node_modules/bson": {
+            "version": "6.9.0",
+            "resolved": "https://registry.npmjs.org/bson/-/bson-6.9.0.tgz",
+            "integrity": "sha512-X9hJeyeM0//Fus+0pc5dSUMhhrrmWwQUtdavaQeF3Ta6m69matZkGWV/MrBcnwUeLC8W9kwwc2hfkZgUuCX3Ig==",
+            "license": "Apache-2.0",
+            "engines": {
+                "node": ">=16.20.1"
+            }
+        },
+        "node_modules/buffer-crc32": {
+            "version": "0.2.13",
+            "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+            "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": "*"
+            }
+        },
         "node_modules/buffer-from": {
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -3315,6 +3409,13 @@
             "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
             "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
         },
+        "node_modules/fast-fifo": {
+            "version": "1.3.2",
+            "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
+            "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
+            "dev": true,
+            "license": "MIT"
+        },
         "node_modules/fast-json-stable-stringify": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -4013,6 +4114,20 @@
                 "node": ">=0.10"
             }
         },
+        "node_modules/https-proxy-agent": {
+            "version": "7.0.5",
+            "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
+            "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "agent-base": "^7.0.2",
+                "debug": "4"
+            },
+            "engines": {
+                "node": ">= 14"
+            }
+        },
         "node_modules/iconv-lite": {
             "version": "0.4.24",
             "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -4799,6 +4914,12 @@
                 "url": "https://github.com/sponsors/streamich"
             }
         },
+        "node_modules/memory-pager": {
+            "version": "1.5.0",
+            "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
+            "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
+            "license": "MIT"
+        },
         "node_modules/merge-descriptors": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
@@ -4979,6 +5100,152 @@
                 "url": "https://github.com/chalk/supports-color?sponsor=1"
             }
         },
+        "node_modules/mongodb": {
+            "version": "6.10.0",
+            "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.10.0.tgz",
+            "integrity": "sha512-gP9vduuYWb9ZkDM546M+MP2qKVk5ZG2wPF63OvSRuUbqCR+11ZCAE1mOfllhlAG0wcoJY5yDL/rV3OmYEwXIzg==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@mongodb-js/saslprep": "^1.1.5",
+                "bson": "^6.7.0",
+                "mongodb-connection-string-url": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=16.20.1"
+            },
+            "peerDependencies": {
+                "@aws-sdk/credential-providers": "^3.188.0",
+                "@mongodb-js/zstd": "^1.1.0",
+                "gcp-metadata": "^5.2.0",
+                "kerberos": "^2.0.1",
+                "mongodb-client-encryption": ">=6.0.0 <7",
+                "snappy": "^7.2.2",
+                "socks": "^2.7.1"
+            },
+            "peerDependenciesMeta": {
+                "@aws-sdk/credential-providers": {
+                    "optional": true
+                },
+                "@mongodb-js/zstd": {
+                    "optional": true
+                },
+                "gcp-metadata": {
+                    "optional": true
+                },
+                "kerberos": {
+                    "optional": true
+                },
+                "mongodb-client-encryption": {
+                    "optional": true
+                },
+                "snappy": {
+                    "optional": true
+                },
+                "socks": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/mongodb-connection-string-url": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz",
+            "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==",
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@types/whatwg-url": "^11.0.2",
+                "whatwg-url": "^13.0.0"
+            }
+        },
+        "node_modules/mongodb-memory-server": {
+            "version": "10.1.2",
+            "resolved": "https://registry.npmjs.org/mongodb-memory-server/-/mongodb-memory-server-10.1.2.tgz",
+            "integrity": "sha512-aDGEWuUVHTiBvaaq03LbpvvSk8IVtepbvp314p1cq7f2xdSpl7igMnYpPfYY5nkks1I5I6OL2ypHjaJj4kBp+g==",
+            "dev": true,
+            "hasInstallScript": true,
+            "license": "MIT",
+            "dependencies": {
+                "mongodb-memory-server-core": "10.1.2",
+                "tslib": "^2.7.0"
+            },
+            "engines": {
+                "node": ">=16.20.1"
+            }
+        },
+        "node_modules/mongodb-memory-server-core": {
+            "version": "10.1.2",
+            "resolved": "https://registry.npmjs.org/mongodb-memory-server-core/-/mongodb-memory-server-core-10.1.2.tgz",
+            "integrity": "sha512-5Wpz712CuDCKTn/40UZ+kMZlav4Y2imbpWuJU5wjuZk6s3+Jg8akTIBW9jQiFS8wgymu6iTg99Iw0XcypsLyQA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "async-mutex": "^0.5.0",
+                "camelcase": "^6.3.0",
+                "debug": "^4.3.7",
+                "find-cache-dir": "^3.3.2",
+                "follow-redirects": "^1.15.9",
+                "https-proxy-agent": "^7.0.5",
+                "mongodb": "^6.9.0",
+                "new-find-package-json": "^2.0.0",
+                "semver": "^7.6.3",
+                "tar-stream": "^3.1.7",
+                "tslib": "^2.7.0",
+                "yauzl": "^3.1.3"
+            },
+            "engines": {
+                "node": ">=16.20.1"
+            }
+        },
+        "node_modules/mongodb-memory-server-core/node_modules/camelcase": {
+            "version": "6.3.0",
+            "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+            "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/mongodb-memory-server-core/node_modules/debug": {
+            "version": "4.3.7",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+            "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "ms": "^2.1.3"
+            },
+            "engines": {
+                "node": ">=6.0"
+            },
+            "peerDependenciesMeta": {
+                "supports-color": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/mongodb-memory-server-core/node_modules/ms": {
+            "version": "2.1.3",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+            "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+            "dev": true,
+            "license": "MIT"
+        },
+        "node_modules/mongodb-memory-server-core/node_modules/semver": {
+            "version": "7.6.3",
+            "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+            "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+            "dev": true,
+            "license": "ISC",
+            "bin": {
+                "semver": "bin/semver.js"
+            },
+            "engines": {
+                "node": ">=10"
+            }
+        },
         "node_modules/ms": {
             "version": "2.1.2",
             "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -5026,6 +5293,19 @@
                 "node": ">= 0.6"
             }
         },
+        "node_modules/new-find-package-json": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/new-find-package-json/-/new-find-package-json-2.0.0.tgz",
+            "integrity": "sha512-lDcBsjBSMlj3LXH2v/FW3txlh2pYTjmbOXPYJD93HI5EwuLzI11tdHSIpUMmfq/IOsldj4Ps8M8flhm+pCK4Ew==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "debug": "^4.3.4"
+            },
+            "engines": {
+                "node": ">=12.22.0"
+            }
+        },
         "node_modules/node-preload": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz",
@@ -5472,6 +5752,13 @@
             "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
             "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
         },
+        "node_modules/pend": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+            "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+            "dev": true,
+            "license": "MIT"
+        },
         "node_modules/performance-now": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
@@ -5677,6 +5964,13 @@
                 }
             ]
         },
+        "node_modules/queue-tick": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
+            "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==",
+            "dev": true,
+            "license": "MIT"
+        },
         "node_modules/randombytes": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -6313,6 +6607,15 @@
                 "node": ">=0.10.0"
             }
         },
+        "node_modules/sparse-bitfield": {
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
+            "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
+            "license": "MIT",
+            "dependencies": {
+                "memory-pager": "^1.0.2"
+            }
+        },
         "node_modules/spawn-wrap": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz",
@@ -6384,6 +6687,21 @@
                 "node": ">=10.0.0"
             }
         },
+        "node_modules/streamx": {
+            "version": "2.20.1",
+            "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz",
+            "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "fast-fifo": "^1.3.2",
+                "queue-tick": "^1.0.1",
+                "text-decoder": "^1.1.0"
+            },
+            "optionalDependencies": {
+                "bare-events": "^2.2.0"
+            }
+        },
         "node_modules/string_decoder": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -6578,6 +6896,18 @@
                 "node": ">=8"
             }
         },
+        "node_modules/tar-stream": {
+            "version": "3.1.7",
+            "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
+            "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "b4a": "^1.6.4",
+                "fast-fifo": "^1.2.0",
+                "streamx": "^2.15.0"
+            }
+        },
         "node_modules/test-exclude": {
             "version": "6.0.0",
             "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
@@ -6612,6 +6942,13 @@
                 "url": "https://github.com/sponsors/isaacs"
             }
         },
+        "node_modules/text-decoder": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.1.tgz",
+            "integrity": "sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==",
+            "dev": true,
+            "license": "Apache-2.0"
+        },
         "node_modules/text-table": {
             "version": "0.2.0",
             "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@@ -6672,6 +7009,18 @@
                 "node": ">=6"
             }
         },
+        "node_modules/tr46": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
+            "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==",
+            "license": "MIT",
+            "dependencies": {
+                "punycode": "^2.3.0"
+            },
+            "engines": {
+                "node": ">=14"
+            }
+        },
         "node_modules/ts-node": {
             "version": "10.9.2",
             "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
@@ -6725,10 +7074,11 @@
             }
         },
         "node_modules/tslib": {
-            "version": "2.6.2",
-            "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
-            "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
-            "devOptional": true
+            "version": "2.8.0",
+            "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz",
+            "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==",
+            "devOptional": true,
+            "license": "0BSD"
         },
         "node_modules/tunnel-agent": {
             "version": "0.6.0",
@@ -7027,6 +7377,28 @@
             "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
             "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
         },
+        "node_modules/webidl-conversions": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+            "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+            "license": "BSD-2-Clause",
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/whatwg-url": {
+            "version": "13.0.0",
+            "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz",
+            "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==",
+            "license": "MIT",
+            "dependencies": {
+                "tr46": "^4.1.1",
+                "webidl-conversions": "^7.0.0"
+            },
+            "engines": {
+                "node": ">=16"
+            }
+        },
         "node_modules/which": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -7216,6 +7588,20 @@
                 "url": "https://github.com/sponsors/sindresorhus"
             }
         },
+        "node_modules/yauzl": {
+            "version": "3.1.3",
+            "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.1.3.tgz",
+            "integrity": "sha512-JCCdmlJJWv7L0q/KylOekyRaUrdEoUxWkWVcgorosTROCFWiS9p2NNPE9Yb91ak7b1N5SxAZEliWpspbZccivw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "buffer-crc32": "~0.2.3",
+                "pend": "~1.2.0"
+            },
+            "engines": {
+                "node": ">=12"
+            }
+        },
         "node_modules/yn": {
             "version": "3.1.1",
             "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
@@ -8012,6 +8398,14 @@
                 "@jridgewell/sourcemap-codec": "^1.4.14"
             }
         },
+        "@mongodb-js/saslprep": {
+            "version": "1.1.9",
+            "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz",
+            "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==",
+            "requires": {
+                "sparse-bitfield": "^3.0.3"
+            }
+        },
         "@noble/hashes": {
             "version": "1.3.3",
             "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz",
@@ -8160,6 +8554,15 @@
             "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==",
             "dev": true
         },
+        "@types/mongodb": {
+            "version": "4.0.6",
+            "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-4.0.6.tgz",
+            "integrity": "sha512-XTbn1Z1j7fHzC1Vkd9LYO48lO2C581r+oRCi/KNzcTHIri7hEaya8r9vxoHJiKr+oeUWVK69+9xr84Mp+aReaw==",
+            "dev": true,
+            "requires": {
+                "mongodb": "*"
+            }
+        },
         "@types/multer": {
             "version": "1.4.11",
             "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.11.tgz",
@@ -8273,6 +8676,19 @@
             "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
             "dev": true
         },
+        "@types/webidl-conversions": {
+            "version": "7.0.3",
+            "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
+            "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="
+        },
+        "@types/whatwg-url": {
+            "version": "11.0.5",
+            "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz",
+            "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==",
+            "requires": {
+                "@types/webidl-conversions": "*"
+            }
+        },
         "@ungap/structured-clone": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
@@ -8307,6 +8723,15 @@
             "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
             "dev": true
         },
+        "agent-base": {
+            "version": "7.1.1",
+            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
+            "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
+            "dev": true,
+            "requires": {
+                "debug": "^4.3.4"
+            }
+        },
         "aggregate-error": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
@@ -8451,6 +8876,15 @@
             "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
             "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="
         },
+        "async-mutex": {
+            "version": "0.5.0",
+            "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz",
+            "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==",
+            "dev": true,
+            "requires": {
+                "tslib": "^2.4.0"
+            }
+        },
         "asynckit": {
             "version": "0.4.0",
             "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -8502,12 +8936,25 @@
                 }
             }
         },
+        "b4a": {
+            "version": "1.6.7",
+            "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz",
+            "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==",
+            "dev": true
+        },
         "balanced-match": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
             "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
             "dev": true
         },
+        "bare-events": {
+            "version": "2.5.0",
+            "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz",
+            "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==",
+            "dev": true,
+            "optional": true
+        },
         "bcrypt-pbkdf": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
@@ -8662,6 +9109,17 @@
                 "update-browserslist-db": "^1.0.13"
             }
         },
+        "bson": {
+            "version": "6.9.0",
+            "resolved": "https://registry.npmjs.org/bson/-/bson-6.9.0.tgz",
+            "integrity": "sha512-X9hJeyeM0//Fus+0pc5dSUMhhrrmWwQUtdavaQeF3Ta6m69matZkGWV/MrBcnwUeLC8W9kwwc2hfkZgUuCX3Ig=="
+        },
+        "buffer-crc32": {
+            "version": "0.2.13",
+            "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+            "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+            "dev": true
+        },
         "buffer-from": {
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -9586,6 +10044,12 @@
             "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
             "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
         },
+        "fast-fifo": {
+            "version": "1.3.2",
+            "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
+            "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
+            "dev": true
+        },
         "fast-json-stable-stringify": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -10081,6 +10545,16 @@
                 "sshpk": "^1.14.1"
             }
         },
+        "https-proxy-agent": {
+            "version": "7.0.5",
+            "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
+            "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
+            "dev": true,
+            "requires": {
+                "agent-base": "^7.0.2",
+                "debug": "4"
+            }
+        },
         "iconv-lite": {
             "version": "0.4.24",
             "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -10646,6 +11120,11 @@
                 "tslib": "^2.0.0"
             }
         },
+        "memory-pager": {
+            "version": "1.5.0",
+            "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
+            "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="
+        },
         "merge-descriptors": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
@@ -10779,6 +11258,84 @@
                 "clone": "^2.1.2"
             }
         },
+        "mongodb": {
+            "version": "6.10.0",
+            "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.10.0.tgz",
+            "integrity": "sha512-gP9vduuYWb9ZkDM546M+MP2qKVk5ZG2wPF63OvSRuUbqCR+11ZCAE1mOfllhlAG0wcoJY5yDL/rV3OmYEwXIzg==",
+            "requires": {
+                "@mongodb-js/saslprep": "^1.1.5",
+                "bson": "^6.7.0",
+                "mongodb-connection-string-url": "^3.0.0"
+            }
+        },
+        "mongodb-connection-string-url": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz",
+            "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==",
+            "requires": {
+                "@types/whatwg-url": "^11.0.2",
+                "whatwg-url": "^13.0.0"
+            }
+        },
+        "mongodb-memory-server": {
+            "version": "10.1.2",
+            "resolved": "https://registry.npmjs.org/mongodb-memory-server/-/mongodb-memory-server-10.1.2.tgz",
+            "integrity": "sha512-aDGEWuUVHTiBvaaq03LbpvvSk8IVtepbvp314p1cq7f2xdSpl7igMnYpPfYY5nkks1I5I6OL2ypHjaJj4kBp+g==",
+            "dev": true,
+            "requires": {
+                "mongodb-memory-server-core": "10.1.2",
+                "tslib": "^2.7.0"
+            }
+        },
+        "mongodb-memory-server-core": {
+            "version": "10.1.2",
+            "resolved": "https://registry.npmjs.org/mongodb-memory-server-core/-/mongodb-memory-server-core-10.1.2.tgz",
+            "integrity": "sha512-5Wpz712CuDCKTn/40UZ+kMZlav4Y2imbpWuJU5wjuZk6s3+Jg8akTIBW9jQiFS8wgymu6iTg99Iw0XcypsLyQA==",
+            "dev": true,
+            "requires": {
+                "async-mutex": "^0.5.0",
+                "camelcase": "^6.3.0",
+                "debug": "^4.3.7",
+                "find-cache-dir": "^3.3.2",
+                "follow-redirects": "^1.15.9",
+                "https-proxy-agent": "^7.0.5",
+                "mongodb": "^6.9.0",
+                "new-find-package-json": "^2.0.0",
+                "semver": "^7.6.3",
+                "tar-stream": "^3.1.7",
+                "tslib": "^2.7.0",
+                "yauzl": "^3.1.3"
+            },
+            "dependencies": {
+                "camelcase": {
+                    "version": "6.3.0",
+                    "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+                    "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+                    "dev": true
+                },
+                "debug": {
+                    "version": "4.3.7",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+                    "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "^2.1.3"
+                    }
+                },
+                "ms": {
+                    "version": "2.1.3",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+                    "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+                    "dev": true
+                },
+                "semver": {
+                    "version": "7.6.3",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+                    "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+                    "dev": true
+                }
+            }
+        },
         "ms": {
             "version": "2.1.2",
             "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -10819,6 +11376,15 @@
             "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
             "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
         },
+        "new-find-package-json": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/new-find-package-json/-/new-find-package-json-2.0.0.tgz",
+            "integrity": "sha512-lDcBsjBSMlj3LXH2v/FW3txlh2pYTjmbOXPYJD93HI5EwuLzI11tdHSIpUMmfq/IOsldj4Ps8M8flhm+pCK4Ew==",
+            "dev": true,
+            "requires": {
+                "debug": "^4.3.4"
+            }
+        },
         "node-preload": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz",
@@ -11161,6 +11727,12 @@
             "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
             "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
         },
+        "pend": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+            "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+            "dev": true
+        },
         "performance-now": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
@@ -11308,6 +11880,12 @@
             "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
             "dev": true
         },
+        "queue-tick": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
+            "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==",
+            "dev": true
+        },
         "randombytes": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -11796,6 +12374,14 @@
             "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
             "dev": true
         },
+        "sparse-bitfield": {
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
+            "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
+            "requires": {
+                "memory-pager": "^1.0.2"
+            }
+        },
         "spawn-wrap": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz",
@@ -11847,6 +12433,18 @@
             "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
             "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="
         },
+        "streamx": {
+            "version": "2.20.1",
+            "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz",
+            "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==",
+            "dev": true,
+            "requires": {
+                "bare-events": "^2.2.0",
+                "fast-fifo": "^1.3.2",
+                "queue-tick": "^1.0.1",
+                "text-decoder": "^1.1.0"
+            }
+        },
         "string_decoder": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -11994,6 +12592,17 @@
                 "has-flag": "^4.0.0"
             }
         },
+        "tar-stream": {
+            "version": "3.1.7",
+            "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
+            "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
+            "dev": true,
+            "requires": {
+                "b4a": "^1.6.4",
+                "fast-fifo": "^1.2.0",
+                "streamx": "^2.15.0"
+            }
+        },
         "test-exclude": {
             "version": "6.0.0",
             "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
@@ -12021,6 +12630,12 @@
                 }
             }
         },
+        "text-decoder": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.1.tgz",
+            "integrity": "sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==",
+            "dev": true
+        },
         "text-table": {
             "version": "0.2.0",
             "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@@ -12066,6 +12681,14 @@
                 "url-parse": "^1.5.3"
             }
         },
+        "tr46": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
+            "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==",
+            "requires": {
+                "punycode": "^2.3.0"
+            }
+        },
         "ts-node": {
             "version": "10.9.2",
             "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
@@ -12096,9 +12719,9 @@
             }
         },
         "tslib": {
-            "version": "2.6.2",
-            "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
-            "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+            "version": "2.8.0",
+            "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz",
+            "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==",
             "devOptional": true
         },
         "tunnel-agent": {
@@ -12313,6 +12936,20 @@
                 }
             }
         },
+        "webidl-conversions": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+            "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="
+        },
+        "whatwg-url": {
+            "version": "13.0.0",
+            "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz",
+            "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==",
+            "requires": {
+                "tr46": "^4.1.1",
+                "webidl-conversions": "^7.0.0"
+            }
+        },
         "which": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -12456,6 +13093,16 @@
                 }
             }
         },
+        "yauzl": {
+            "version": "3.1.3",
+            "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.1.3.tgz",
+            "integrity": "sha512-JCCdmlJJWv7L0q/KylOekyRaUrdEoUxWkWVcgorosTROCFWiS9p2NNPE9Yb91ak7b1N5SxAZEliWpspbZccivw==",
+            "dev": true,
+            "requires": {
+                "buffer-crc32": "~0.2.3",
+                "pend": "~1.2.0"
+            }
+        },
         "yn": {
             "version": "3.1.1",
             "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
diff --git a/apps/token-server/package.json b/apps/token-server/package.json
--- a/apps/token-server/package.json
+++ b/apps/token-server/package.json
@@ -32,6 +32,7 @@
         "@types/cors": "^2.8.17",
         "@types/express": "^4.17.21",
         "@types/mocha": "^10.0.6",
+        "@types/mongodb": "^4.0.6",
         "@types/multer": "^1.4.11",
         "@types/node": "^20.11.29",
         "@types/node-telegram-bot-api": "^0.64.6",
@@ -43,6 +44,7 @@
         "mocha": "^10.3.0",
         "mocha-junit-reporter": "^2.2.1",
         "mocha-suppress-logs": "^0.5.1",
+        "mongodb-memory-server": "^10.1.2",
         "nyc": "^15.1.0",
         "supertest": "^6.3.4",
         "ts-node": "^10.9.2",
@@ -62,6 +64,7 @@
         "express": "^4.18.3",
         "express-rate-limit": "^7.4.0",
         "helmet": "^7.1.0",
+        "mongodb": "^6.10.0",
         "multer": "^1.4.5-lts.1",
         "node-telegram-bot-api": "^0.65.1",
         "sharp": "^0.33.2",
diff --git a/apps/token-server/secrets.sample.ts b/apps/token-server/secrets.sample.ts
--- a/apps/token-server/secrets.sample.ts
+++ b/apps/token-server/secrets.sample.ts
@@ -10,6 +10,12 @@
     approvedMods: number[];
     wallet: ServerWallet;
     recaptchaSecret: string;
+    db: {
+        username: string;
+        password: string;
+        containerName: string;
+        port: number;
+    };
 }
 
 interface TokenServerSecrets {
@@ -32,6 +38,12 @@
             ),
         },
         recaptchaSecret: 'reCAPTCHA_COPY_SECRET_KEY',
+        db: {
+            username: 'username',
+            password: 'password',
+            containerName: 'localhost',
+            port: 27017,
+        },
     },
     prod: {
         botId: 'yourBotId',
@@ -50,6 +62,12 @@
             ),
         },
         recaptchaSecret: 'reCAPTCHA_COPY_SECRET_KEY',
+        db: {
+            username: 'username',
+            password: 'password',
+            containerName: 'localhost',
+            port: 27017,
+        },
     },
 };
 
diff --git a/apps/token-server/src/db.ts b/apps/token-server/src/db.ts
new file mode 100644
--- /dev/null
+++ b/apps/token-server/src/db.ts
@@ -0,0 +1,176 @@
+// Copyright (c) 2024 The Bitcoin developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+import { MongoClient, Db, Collection, CollectionInfo } from 'mongodb';
+import config from '../config';
+
+interface BlacklistEntry {
+    /** tokenId of blacklisted token */
+    tokenId: string;
+    /** A short explanation of why this token was blacklisted, e.g. "impersonating tether" */
+    reason: string;
+    /**
+     * When this token was added to the blacklist
+     * We use number instead of Date as the API returns JSON
+     */
+    timestamp: number;
+    /** string describing who added this token to the blacklist */
+    addedBy: string;
+}
+
+const initialBlacklistTokens = [
+    {
+        tokenId:
+            '09c53c9a9fe0df2cb729dd6f99f2b836c59b842d6652becd85658e277caab611',
+        reason: 'Impersonates Blazer (site that runs poker tournaments)',
+    },
+    {
+        tokenId:
+            '9c662233f8553e72ab3848a37d72fbc3f894611aae43033cde707213a537bba0',
+        reason: 'Impersonates BUX stablecoin',
+    },
+    {
+        tokenId:
+            '6dcb149e77a8f86a85d2fb8505dadb194994a922102fcea6309f2818de9ee173',
+        reason: 'Impersonating a USD stablecoin',
+    },
+    {
+        tokenId:
+            '059308a0d6ef0443d8bd014ac85f830d98780b1ce53bc2326680ed27e99803f6',
+        reason: 'Impersonating a USD stablecoin',
+    },
+    {
+        tokenId:
+            '2a328dbe125bd0ef8d199b2b4f20ce84bb36a7c0d12246668163a6077d4f494b',
+        reason: 'Impersonating a USD stablecoin',
+    },
+    {
+        tokenId:
+            '3387978c85f382632ecb5cdc23c4912c4c22688790d9264f84c3c1351c049719',
+        reason: 'Impersonating a USD stablecoin',
+    },
+    {
+        tokenId:
+            '07da70e787181ac67a34f9292b4e13a93cd081e4ca540a8ddafe4cc86ee26e2d',
+        reason: 'Impersonating a USD stablecoin',
+    },
+    {
+        tokenId:
+            '4e56e9bedfb654560eb1917b2e2fa40473cf26a8a9a0f84e0b0e91a9cce1df65',
+        reason: 'Impersonating a USD stablecoin',
+    },
+    {
+        tokenId:
+            '2a33476bcd30bfbc5e57fb33da26f641020a53c925db7394e6d3b8eecf82e2ec',
+        reason: 'Impersonating a USD stablecoin',
+    },
+    {
+        tokenId:
+            'b69dcc90c72e852e1dc712704cb376e588cee6266a51e647c61a724c00625cc8',
+        reason: 'Impersonating a USD stablecoin',
+    },
+    {
+        tokenId:
+            '7c14895521c158798478a64d146f67f22e1c8c5b962422ed47636fda71d82f1d',
+        reason: 'Impersonating Meta and attempting to use their logo',
+    },
+    {
+        tokenId:
+            '6f231d49fefd938a9a6b4e6b93d14c7127e11bd5621056eb9c6528164b9d7ce0',
+        reason: 'Impersonating Meta and attempting to use their logo',
+    },
+    {
+        tokenId:
+            'a6a16ac38d37e35c9f9eb81e9014827cef9da105a94607ec16a2c6e76224d098',
+        reason: 'Impersonating corporate brand using their logo',
+    },
+    {
+        tokenId:
+            'db2e95abe66f6b1f21a860a177b7a73565182185a99b6043b5183f59df7ecfbf',
+        reason: 'Impersonating corporate brand using their logo',
+    },
+    {
+        tokenId:
+            '4c008a1cd5002063d2942daed16ff0e118bc3e41c7c0a4155ac096ee5a389c21',
+        reason: 'Impersonating RAIPAY',
+    },
+];
+const initialBlacklist = initialBlacklistTokens.map(item => ({
+    ...item,
+    // When added to this file
+    timestamp: Math.round(new Date(1730090292122).getTime() / 1000),
+    addedBy: 'Initial Setup',
+}));
+export { initialBlacklist };
+
+export const initializeDb = async (
+    client: MongoClient,
+    blacklist = initialBlacklist,
+): Promise<Db> => {
+    await client.connect();
+    console.log('Successfully connected to mongod database');
+    const db: Db = client.db(config.db.name);
+
+    const collections: CollectionInfo[] = await db.listCollections().toArray();
+
+    const blacklistCollectionName = config.db.collections.blacklist.name;
+
+    // Check if the collection exists in the list
+    const blacklistExists: boolean = collections.some(
+        collection => collection.name === blacklistCollectionName,
+    );
+
+    if (!blacklistExists) {
+        // If the blacklist does not exist, initialize it
+        const blacklistedTokenIds: Collection = db.collection(
+            blacklistCollectionName,
+        );
+        // Index by tokenId which is unique, ensuring we do not enter the same tokenId more than once
+        // This also improves query times
+        blacklistedTokenIds.createIndex({ tokenId: 1 }, { unique: true });
+
+        // Initialize blacklist
+        const result = await blacklistedTokenIds.insertMany(blacklist);
+        console.log(
+            `${result.insertedCount} tokens inserted into ${blacklistCollectionName}`,
+        );
+    } else {
+        // If the blacklist exists, log how many entries we have
+        const blacklistedTokenCount = await db
+            .collection(blacklistCollectionName)
+            .countDocuments();
+        console.log(
+            `Collection "${blacklistCollectionName}" exists and includes ${blacklistedTokenCount} tokens. Continuing token-server startup...`,
+        );
+    }
+
+    return db;
+};
+
+export const getBlacklistedTokenIds = async (db: Db): Promise<string[]> => {
+    const collection = db.collection(config.db.collections.blacklist.name);
+
+    // Query only for tokenId fields
+    const projection = { _id: 0, tokenId: 1 };
+    const tokenIds = await collection
+        .find({}, { projection })
+        .map(doc => doc.tokenId)
+        .toArray();
+
+    return tokenIds;
+};
+
+export const getOneBlacklistEntry = async (
+    db: Db,
+    tokenId: string,
+): Promise<BlacklistEntry | null> => {
+    const collection = db.collection(config.db.collections.blacklist.name);
+    // Don't return _id
+    const projection = { _id: 0 };
+
+    // Query for a single document where tokenId matches
+    const result = await collection.findOne({ tokenId }, { projection });
+
+    return result as BlacklistEntry | null;
+};
diff --git a/apps/token-server/src/routes.ts b/apps/token-server/src/routes.ts
--- a/apps/token-server/src/routes.ts
+++ b/apps/token-server/src/routes.ts
@@ -14,14 +14,16 @@
 import { isAddressEligibleForTokenReward } from './rewards';
 import { sendReward, sendXecAirdrop } from './transactions';
 import { ChronikClient } from 'chronik-client';
-import { isTokenImageRequest } from './validation';
+import { isTokenImageRequest, isValidTokenId } from './validation';
 import makeBlockie from 'ethereum-blockies-base64';
 import TelegramBot from 'node-telegram-bot-api';
-import { alertNewTokenIcon } from '../src/telegram';
+import { alertNewTokenIcon } from './telegram';
+import { getBlacklistedTokenIds, getOneBlacklistEntry } from './db';
 import cashaddr from 'ecashaddrjs';
 import { Ecc } from 'ecash-lib';
 import { RateLimitRequestHandler } from 'express-rate-limit';
 import axios from 'axios';
+import { Db } from 'mongodb';
 
 /**
  * routes.ts
@@ -62,6 +64,7 @@
 
 export const startExpressServer = (
     port: Number,
+    db: Db,
     chronik: ChronikClient,
     telegramBot: TelegramBot,
     fs: any,
@@ -101,6 +104,58 @@
         });
     });
 
+    app.get('/blacklist', async function (req: Request, res: Response) {
+        logIpInfo(req);
+        try {
+            const tokenIds = await getBlacklistedTokenIds(db);
+            return res.status(200).json({ status: 'success', tokenIds });
+        } catch (err) {
+            console.error('Error retrieving tokenIds:', err);
+            return res.status(500).json({
+                status: 'error',
+                message: 'Failed to retrieve tokenIds',
+            });
+        }
+    });
+
+    app.get(
+        '/blacklist/:tokenId',
+        async function (req: Request, res: Response) {
+            logIpInfo(req);
+            const tokenId = req.params.tokenId;
+
+            if (!isValidTokenId(tokenId)) {
+                return res.status(500).json({
+                    status: 'error',
+                    message: `Invalid tokenId: ${tokenId}`,
+                });
+            }
+            try {
+                // Check the blacklist
+                const entry = await getOneBlacklistEntry(db, tokenId);
+                console.log(`entry`, entry);
+                if (entry) {
+                    return res.status(200).json({
+                        status: 'success',
+                        isBlacklisted: true,
+                        entry,
+                    });
+                } else {
+                    return res.status(200).json({
+                        status: 'success',
+                        isBlacklisted: false,
+                    });
+                }
+            } catch (err) {
+                console.error(`Error retrieving /blacklist/${tokenId}`, err);
+                return res.status(500).json({
+                    status: 'error',
+                    message: `Failed to retrieve tokenId ${tokenId} from the database`,
+                });
+            }
+        },
+    );
+
     app.get(
         '/is-eligible/:address',
         async function (req: Request, res: Response) {
diff --git a/apps/token-server/src/validation.ts b/apps/token-server/src/validation.ts
--- a/apps/token-server/src/validation.ts
+++ b/apps/token-server/src/validation.ts
@@ -7,6 +7,9 @@
 // Match if input is a string that ends with a 64-char lowercase hex string and .png extension
 const TOKEN_ICON_REQUEST_REGEX = new RegExp(/^\/([0-9]+)\/[a-f0-9]{64}.png$/);
 
+// TokenId regex
+const TOKEN_ID_REGEX = new RegExp(/[a-f0-9]{64}/);
+
 /**
  * Determine if a request caught by 404 was for a token icon
  * @param req express request, e.g. /slpv1/512/3fee3384150b030490b7bee095a63900f66a45f2d8e3002ae2cf17ce3ef4d109.png
@@ -15,3 +18,7 @@
 export const isTokenImageRequest = (req: Request): boolean => {
     return TOKEN_ICON_REQUEST_REGEX.test(req.url);
 };
+
+export const isValidTokenId = (tokenId: string): boolean => {
+    return TOKEN_ID_REGEX.test(tokenId);
+};
diff --git a/apps/token-server/test/db.test.ts b/apps/token-server/test/db.test.ts
new file mode 100644
--- /dev/null
+++ b/apps/token-server/test/db.test.ts
@@ -0,0 +1,68 @@
+// Copyright (c) 2024 The Bitcoin developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+// Mock mongodb
+import { MongoClient, Db } from 'mongodb';
+import { MongoMemoryServer } from 'mongodb-memory-server';
+import {
+    initializeDb,
+    getBlacklistedTokenIds,
+    initialBlacklist,
+    getOneBlacklistEntry,
+} from '../src/db';
+import * as assert from 'assert';
+import config from '../config';
+
+// Clone initialBlacklist before initializing the database
+// initializeDb(initialBlacklist) will modify the entries by adding an "_id" key
+const mockBlacklist = initialBlacklist.map(entry => ({ ...entry }));
+
+describe('db.ts, token-server database unit tests', async function () {
+    let mongoServer: MongoMemoryServer, testMongoClient: MongoClient;
+    before(async () => {
+        // Start mongo memory server before running this suite of unit tests
+        mongoServer = await MongoMemoryServer.create();
+        const mongoUri = mongoServer.getUri();
+        testMongoClient = new MongoClient(mongoUri);
+    });
+
+    after(async () => {
+        // Shut down mongo memory server after running this suite of unit tests
+        await testMongoClient.close();
+        await mongoServer.stop();
+    });
+
+    let testDb: Db;
+    beforeEach(async () => {
+        testDb = await initializeDb(testMongoClient, initialBlacklist);
+    });
+
+    afterEach(async () => {
+        // Wipe the database after each unit test
+        await testDb.dropDatabase();
+    });
+
+    it('initializeDb returns a mongo db instance of the expected schema', async function () {
+        const { namespace } = testDb;
+        assert.strictEqual(namespace, config.db.name);
+    });
+    it('getBlacklistedTokenIds can fetch an array of all blacklisted token ids', async function () {
+        const tokenIds = await getBlacklistedTokenIds(testDb);
+        assert.deepEqual(
+            tokenIds,
+            mockBlacklist.map(entry => entry.tokenId),
+        );
+    });
+    it('getOneBlacklistEntry returns expected information for a blacklisted tokenId', async function () {
+        const blacklistedTokenId = mockBlacklist[0].tokenId;
+        const entry = await getOneBlacklistEntry(testDb, blacklistedTokenId);
+        assert.deepEqual(entry, mockBlacklist[0]);
+    });
+    it('getOneBlacklistEntry returns null if tokenId cannot be found on the blacklist', async function () {
+        const blacklistedTokenId =
+            '0000000000000000000000000000000000000000000000000000000000000000';
+        const entry = await getOneBlacklistEntry(testDb, blacklistedTokenId);
+        assert.equal(entry, null);
+    });
+});
diff --git a/apps/token-server/test/routes.test.ts b/apps/token-server/test/routes.test.ts
--- a/apps/token-server/test/routes.test.ts
+++ b/apps/token-server/test/routes.test.ts
@@ -20,19 +20,42 @@
 } from './vectors';
 import { Ecc, initWasm } from 'ecash-lib';
 import { rateLimit } from 'express-rate-limit';
+import { MongoClient, Db } from 'mongodb';
+import { MongoMemoryServer } from 'mongodb-memory-server';
+import {
+    initializeDb,
+    initialBlacklist,
+    getBlacklistedTokenIds,
+} from '../src/db';
 const axios = require('axios');
 const MockAdapter = require('axios-mock-adapter');
 
+// Clone initialBlacklist before initializing the database
+// initializeDb(initialBlacklist) will modify the entries by adding an "_id" key
+const mockBlacklist = initialBlacklist.map(entry => ({ ...entry }));
+
 describe('routes.js', async function () {
     let ecc: Ecc;
+    let mongoServer: MongoMemoryServer, testMongoClient: MongoClient;
     before(async () => {
         // Initialize web assembly
         await initWasm();
         // Initialize Ecc
         ecc = new Ecc();
+        // Start mongo memory server before running this suite of unit tests
+        mongoServer = await MongoMemoryServer.create();
+        const mongoUri = mongoServer.getUri();
+        testMongoClient = new MongoClient(mongoUri);
+    });
+
+    after(async () => {
+        // Shut down mongo memory server after running this suite of unit tests
+        await testMongoClient.close();
+        await mongoServer.stop();
     });
 
     let app: http.Server;
+    let badDbApp: http.Server;
     const SERVER_WALLET_ADDRESS = secrets.prod.wallet.address;
     const SERVER_WALLET_OUTPUTSCRIPT = cashaddr.getOutputScriptFromAddress(
         SERVER_WALLET_ADDRESS,
@@ -146,7 +169,9 @@
 
     // Initialize fs, to be memfs in these tests
     let fs: any;
+    let testDb: Db;
     beforeEach(async () => {
+        testDb = await initializeDb(testMongoClient, initialBlacklist);
         // Mock expected file structure for fs
         const fileStructureJson: any = {};
         // Create mock empty directories for all supported sizes
@@ -158,6 +183,32 @@
         const TEST_PORT = 5000;
         app = startExpressServer(
             TEST_PORT,
+            testDb,
+            mockedChronikClient,
+            mockedTgBot as unknown as TelegramBot,
+            fs,
+            ecc,
+            // We need higher rate limits so we do not rate limit ourselves in the tests
+            rateLimit({
+                windowMs: 60000,
+                limit: 100, // Limit each IP to 10 requests per `window`
+                standardHeaders: 'draft-7', // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header
+                legacyHeaders: false, // Disable the `X-RateLimit-*` headers.
+                message: 'You have rate limited your own unit tests.',
+            }),
+            // In tests, keep the same rate limits for token rewards
+            rateLimit({
+                windowMs: 60000,
+                limit: 100, // Limit each IP to 10 requests per `window`
+                standardHeaders: 'draft-7', // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header
+                legacyHeaders: false, // Disable the `X-RateLimit-*` headers.
+                message: 'You have rate limited your own unit tests.',
+            }),
+        );
+        const TEST_PORT_BAD_DB = 5001;
+        badDbApp = startExpressServer(
+            TEST_PORT_BAD_DB,
+            {} as unknown as Db,
             mockedChronikClient,
             mockedTgBot as unknown as TelegramBot,
             fs,
@@ -185,6 +236,9 @@
         vol.reset();
         // Stop express server
         app.close();
+        badDbApp.close();
+        // Wipe the database after each unit test
+        await testDb.dropDatabase();
     });
     it('/status returns expected status', function () {
         return request(app)
@@ -859,4 +913,70 @@
                 msg: 'Error resizing uploaded token icon',
             });
     });
+    it('/blacklist returns tokenIds of the blacklist', function () {
+        return request(app)
+            .get(`/blacklist`)
+            .expect(200)
+            .expect('Content-Type', /json/)
+            .expect({
+                status: 'success',
+                tokenIds: mockBlacklist.map(entry => entry.tokenId),
+            });
+    });
+    it('/blacklist returns tokenIds of the blacklist', function () {
+        return request(badDbApp)
+            .get(`/blacklist`)
+            .expect(500)
+            .expect('Content-Type', /json/)
+            .expect({
+                status: 'error',
+                message: 'Failed to retrieve tokenIds',
+            });
+    });
+    it('/blacklist/:tokenId returns expected entry for a valid tokenId in the blacklist', function () {
+        const tokenId = mockBlacklist[0].tokenId;
+        return request(app)
+            .get(`/blacklist/${tokenId}`)
+            .expect(200)
+            .expect('Content-Type', /json/)
+            .expect({
+                status: 'success',
+                isBlacklisted: true,
+                entry: mockBlacklist[0],
+            });
+    });
+    it('/blacklist/:tokenId returns expected error for an invalid tokenId', function () {
+        const tokenId = 'not a token id';
+        return request(app)
+            .get(`/blacklist/${tokenId}`)
+            .expect(500)
+            .expect('Content-Type', /json/)
+            .expect({
+                status: 'error',
+                message: `Invalid tokenId: ${tokenId}`,
+            });
+    });
+    it('/blacklist/:tokenId returns expected entry for a valid tokenId NOT in the blacklist', function () {
+        const tokenId =
+            '0000000000000000000000000000000000000000000000000000000000000000';
+        return request(app)
+            .get(`/blacklist/${tokenId}`)
+            .expect(200)
+            .expect('Content-Type', /json/)
+            .expect({
+                status: 'success',
+                isBlacklisted: false,
+            });
+    });
+    it('/blacklist/:tokenId returns expected error if database fails to lookup a valid tokenId', function () {
+        const tokenId = mockBlacklist[0].tokenId;
+        return request(badDbApp)
+            .get(`/blacklist/${tokenId}`)
+            .expect(500)
+            .expect('Content-Type', /json/)
+            .expect({
+                status: 'error',
+                message: `Failed to retrieve tokenId ${tokenId} from the database`,
+            });
+    });
 });
diff --git a/apps/token-server/test/validation.test.ts b/apps/token-server/test/validation.test.ts
--- a/apps/token-server/test/validation.test.ts
+++ b/apps/token-server/test/validation.test.ts
@@ -3,7 +3,7 @@
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
 import * as assert from 'assert';
-import { isTokenImageRequest } from '../src/validation';
+import { isTokenImageRequest, isValidTokenId } from '../src/validation';
 import vectors from './vectors';
 
 describe('validation.ts', function () {
@@ -16,4 +16,13 @@
             });
         });
     });
+    describe('We can validate a tokenId', function () {
+        const { returns } = vectors.isValidTokenId;
+        returns.forEach(vector => {
+            const { description, string, returned } = vector;
+            it(description, function () {
+                assert.equal(isValidTokenId(string), returned);
+            });
+        });
+    });
 });
diff --git a/apps/token-server/test/vectors.ts b/apps/token-server/test/vectors.ts
--- a/apps/token-server/test/vectors.ts
+++ b/apps/token-server/test/vectors.ts
@@ -320,6 +320,16 @@
     error: Error;
 }
 
+interface IsValidTokenIdVector {
+    returns: IsValidTokenIdReturn[];
+}
+
+interface IsValidTokenIdReturn {
+    description: string;
+    string: string;
+    returned: boolean;
+}
+
 interface TestVectors {
     hasInputsFromOutputScript: HasInputsFromOutputScriptVector;
     addressReceivedToken: AddressReceivedTokenReturnVector;
@@ -332,6 +342,7 @@
     getSlpInputsAndOutputs: GetSlpInputsAndOutputsVector;
     sendReward: SendRewardVector;
     sendXecAirdrop: SendXecAirdropVector;
+    isValidTokenId: IsValidTokenIdVector;
 }
 
 const vectors: TestVectors = {
@@ -1236,6 +1247,45 @@
             },
         ],
     },
+    isValidTokenId: {
+        returns: [
+            {
+                description: 'Valid tokenId',
+                string: '0000000000000000000000000000000000000000000000000000000000000000',
+                returned: true,
+            },
+            {
+                description: 'Valid hex but 63 chars is invalid',
+                string: '000000000000000000000000000000000000000000000000000000000000000',
+                returned: false,
+            },
+            {
+                description: 'Valid hex but 31 bytes (62 chars) is invalid',
+                string: '00000000000000000000000000000000000000000000000000000000000000',
+                returned: false,
+            },
+            {
+                description: 'Valid hex but 65 chars is invalid',
+                string: '00000000000000000000000000000000000000000000000000000000000000000',
+                returned: true,
+            },
+            {
+                description: 'Valid hex but 33 bytes (66 chars) is invalid',
+                string: '000000000000000000000000000000000000000000000000000000000000000000',
+                returned: true,
+            },
+            {
+                description: 'Valid length but invalid hex is invalid',
+                string: 'g000000000000000000000000000000000000000000000000000000000000000',
+                returned: false,
+            },
+            {
+                description: 'Empty string is invalid',
+                string: '',
+                returned: false,
+            },
+        ],
+    },
 };
 
 export default vectors;
diff --git a/cashtab/src/config/token.js b/cashtab/src/config/token.js
--- a/cashtab/src/config/token.js
+++ b/cashtab/src/config/token.js
@@ -21,6 +21,8 @@
         '2a328dbe125bd0ef8d199b2b4f20ce84bb36a7c0d12246668163a6077d4f494b',
         '3387978c85f382632ecb5cdc23c4912c4c22688790d9264f84c3c1351c049719',
         '07da70e787181ac67a34f9292b4e13a93cd081e4ca540a8ddafe4cc86ee26e2d',
+        '2a33476bcd30bfbc5e57fb33da26f641020a53c925db7394e6d3b8eecf82e2ec',
+        'b69dcc90c72e852e1dc712704cb376e588cee6266a51e647c61a724c00625cc8',
         // fake facebook/meta
         '7c14895521c158798478a64d146f67f22e1c8c5b962422ed47636fda71d82f1d',
         '6f231d49fefd938a9a6b4e6b93d14c7127e11bd5621056eb9c6528164b9d7ce0',