From d78b0880af5faf46dcfed64eca2463a141a38c15 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Mon, 4 May 2026 17:40:14 -0400 Subject: [PATCH 1/8] refactor Modules class to be a standalone function --- .../bitcore-client/test/unit/wallet.test.ts | 18 ++-- .../src/modules/bitcoin-cash/index.ts | 18 ++-- .../bitcore-node/src/modules/bitcoin/index.ts | 18 ++-- .../src/modules/dogecoin/index.ts | 18 ++-- .../src/modules/ethereum/index.ts | 20 +++-- packages/bitcore-node/src/modules/index.ts | 89 +++++-------------- .../src/modules/litecoin/index.ts | 18 ++-- .../bitcore-node/src/modules/matic/index.ts | 18 ++-- .../bitcore-node/src/modules/moralis/index.ts | 20 +++-- .../src/modules/multiProvider/index.ts | 25 +++--- .../src/modules/ripple/api/event-adapter.ts | 60 +------------ .../bitcore-node/src/modules/ripple/index.ts | 25 +++--- .../bitcore-node/src/modules/solana/index.ts | 14 +-- packages/bitcore-node/src/workers/all.ts | 4 +- packages/bitcore-node/src/workers/api.ts | 6 +- packages/bitcore-node/src/workers/p2p.ts | 4 +- .../bitcore-node/test/helpers/integration.ts | 4 +- packages/bitcore-node/test/helpers/unit.ts | 4 +- .../bitcore-node/test/unit/modules.test.ts | 23 +++-- .../test/verification/db-repair.ts | 4 +- .../test/verification/db-verify-headers.ts | 6 +- .../test/verification/db-verify.ts | 4 +- .../test/verification/prune-fork.ts | 4 +- 23 files changed, 168 insertions(+), 256 deletions(-) diff --git a/packages/bitcore-client/test/unit/wallet.test.ts b/packages/bitcore-client/test/unit/wallet.test.ts index 9f9a4a2dead..c69f09cf1ba 100644 --- a/packages/bitcore-client/test/unit/wallet.test.ts +++ b/packages/bitcore-client/test/unit/wallet.test.ts @@ -8,7 +8,7 @@ import { Encryption } from '../../src/encryption'; import { Api as bcnApi } from '../../../bitcore-node/build/src/services/api'; import { Storage as bcnStorage } from '../../../bitcore-node/build/src/services/storage'; import crypto from 'crypto'; -import { Modules } from '../../../bitcore-node/build/src/modules'; +import { loadModules } from '../../../bitcore-node/build/src/modules'; import request from 'request-promise-native'; import requestStream from 'request'; import { Server } from 'http'; @@ -18,7 +18,6 @@ import supertest from 'supertest'; import { utils } from '../../src/utils'; import ethMigrationTestWalletFixture from './data/ethMigrationTestWallet.fixture'; - const should = chai.should(); const expect = chai.expect; @@ -56,7 +55,7 @@ describe('Wallet', function() { dbPort: process.env.DB_PORT || '27017', dbName: process.env.DB_NAME || 'bitcore-client-tests' }); - Modules.loadConfigured(); + loadModules(); const httpServer: Server = await bcnApi.start(); api = supertest(httpServer); }); @@ -161,7 +160,7 @@ describe('Wallet', function() { expect(err.message).to.equal('Must provide changeIdx for UTXO chains'); } }); - + it('should throw an error if neither rawTx nor txid is provided', async () => { try { await wallet.bumpTxFee({ changeIdx: 0 }); @@ -196,12 +195,12 @@ describe('Wallet', function() { sandbox.stub(wallet, 'getTransactionByTxid').resolves({ '_id': '65982807d85e75f781a0d56f', 'txid': '8b78e4d2ac2211472454f940445210b6487aaa0f889e18066eb3f623352607f5', 'network': 'regtest', 'chain': 'BTC', 'blockHeight': -1, 'blockHash': '', 'blockTime': '2024-01-05T16:02:15.678Z', 'blockTimeNormalized': '2024-01-05T16:02:15.678Z', 'coinbase': false, 'locktime': -1, 'inputCount': 5, 'outputCount': 1, 'size': 780, 'fee': 2409, 'value': 950891, 'confirmations': 0, 'coins': { 'inputs': [{ '_id': '659825bed85e75f781a01812', 'chain': 'BTC', 'network': 'regtest', 'coinbase': false, 'mintIndex': 1, 'spentTxid': '8b78e4d2ac2211472454f940445210b6487aaa0f889e18066eb3f623352607f5', 'mintTxid': '330716a75c23512202e45ffee478bdce7a33f298edf7eef30a42dbde06746c48', 'mintHeight': 154, 'spentHeight': -1, 'address': 'n2vpyzbBPBEni9FgkeQSnXvAQAfnRmKxFZ', 'script': '76a914eade85745090388e64a9341a82d8f94371430d1a88ac', 'value': 244000, 'confirmations': -1, 'sequenceNumber': 4294967293 }, { '_id': '65982587d85e75f781a0046f', 'chain': 'BTC', 'network': 'regtest', 'coinbase': false, 'mintIndex': 1, 'spentTxid': '8b78e4d2ac2211472454f940445210b6487aaa0f889e18066eb3f623352607f5', 'mintTxid': '7b83f82a22f94fc3e615743a5e6fb2345ed79aad8a2b564af2ace49da1e27ebb', 'mintHeight': 152, 'spentHeight': -1, 'address': 'muEU9KaNEw5doymy59ScrgEE3Eq4CQ45U9', 'script': '76a91496739b4ef4e793c05a78624da38d96f2c4059c6b88ac', 'value': 239400, 'confirmations': -1, 'sequenceNumber': 4294967293 }, { '_id': '65982511d85e75f7819fdd40', 'chain': 'BTC', 'network': 'regtest', 'coinbase': false, 'mintIndex': 0, 'spentTxid': '8b78e4d2ac2211472454f940445210b6487aaa0f889e18066eb3f623352607f5', 'mintTxid': 'fb4d8039024c80a655fea29e01faf202f30e936af5738e31c5841a84215ca4d6', 'mintHeight': 149, 'spentHeight': -1, 'address': 'muEU9KaNEw5doymy59ScrgEE3Eq4CQ45U9', 'script': '76a91496739b4ef4e793c05a78624da38d96f2c4059c6b88ac', 'value': 239400, 'confirmations': -1, 'sequenceNumber': 4294967293 }, { '_id': '659824d7d85e75f7819fc75d', 'chain': 'BTC', 'network': 'regtest', 'coinbase': false, 'mintIndex': 0, 'spentTxid': '8b78e4d2ac2211472454f940445210b6487aaa0f889e18066eb3f623352607f5', 'mintTxid': '8221ed6891f6ba82ab72bb8298a661e21b47b04408861b0c3268c4a04a0435b3', 'mintHeight': 145, 'spentHeight': -1, 'address': 'miEzS8fSvtDjwkEjY14FEuWumFKg8nTPV9', 'script': '76a9141de381fc20d34d20e11a5543f536a4b74f495a9888ac', 'value': 228000, 'confirmations': -1, 'sequenceNumber': 4294967293 }, { '_id': '65982389d85e75f7819f57ff', 'chain': 'BTC', 'network': 'regtest', 'coinbase': false, 'mintIndex': 0, 'spentTxid': '8b78e4d2ac2211472454f940445210b6487aaa0f889e18066eb3f623352607f5', 'mintTxid': '95f713c2d7e09ab57ec3016bb767b26e6ee849cf487e86d8ed6126ae1b893e0f', 'mintHeight': 143, 'spentHeight': -1, 'address': 'msnM9VB5usfBahZNo57ZUq9uzwRxQXksUz', 'script': '76a914868ad3308906626182d1c6cd703cc7ce78b3a28d88ac', 'value': 2500, 'confirmations': -1, 'sequenceNumber': 4294967293 }], 'outputs': [{ '_id': '65982807d85e75f781a0d55d', 'chain': 'BTC', 'network': 'regtest', 'coinbase': false, 'mintIndex': 0, 'spentTxid': '', 'mintTxid': '8b78e4d2ac2211472454f940445210b6487aaa0f889e18066eb3f623352607f5', 'mintHeight': -1, 'spentHeight': -2, 'address': 'mwwrGzk9q8xugY97C8BiNbcgqXNyjH4kp8', 'script': '76a914b4376347e4cffb1e9a475b2661bbe74de3c1f86a88ac', 'value': 950891, 'confirmations': -1 }] } }); sandbox.stub(wallet.client, 'importAddresses').resolves(); sandbox.spy(CWC.BitcoreLib.Transaction.prototype, 'feePerByte'); - + await wallet.generateAddressPair(1310066242, true); await wallet.generateAddressPair(1087984800, true); await wallet.generateAddressPair(1310064953, true); await wallet.generateAddressPair(1310062823, true); - + const { tx: newTx } = await wallet.bumpTxFee({ txid: '8b78e4d2ac2211472454f940445210b6487aaa0f889e18066eb3f623352607f5', changeIdx: 0, @@ -230,7 +229,7 @@ describe('Wallet', function() { await wallet.unlock('abc123'); }); - + it('should throw an error if neither rawTx nor txid is provided', async () => { try { await wallet.bumpTxFee({ changeIdx: 0 }); @@ -256,7 +255,7 @@ describe('Wallet', function() { it('should bump the fee of a transaction with feeRate', async function() { sandbox.stub(wallet, 'getTransactionByTxid').resolves({ 'txid': '0x0cf410cfe7fb268ad06ae115edfa8a30a8dea3979336a647b09b5a789c4b53d5', 'network': 'regtest', 'chain': 'ETH', 'blockHeight': 43245, 'blockHash': '0x9edb8d10883a360f7ff0c26860b6a159f5b7a74226949a4365691a879fafcdfc', 'blockTime': '2024-01-08T16:31:46.000Z', 'blockTimeNormalized': '2024-01-08T16:31:46.000Z', 'fee': 42000000000000, 'value': 1000000000000000000, 'gasLimit': 200000, 'gasPrice': 2000000000, 'nonce': 0, 'to': '0x7ee308b49e36Ab516cd0186B3a47CFD31d2499A1', 'from': '0x5FbdD2712d05D1a73e0b3Eba5efE8c3d42a336C3', 'effects': [], 'data': '0x', 'internal': [], 'calls': [], 'confirmations': 33 }); sandbox.stub(wallet.client, 'importAddresses').resolves(); - + const { tx: newTx, params } = await wallet.bumpTxFee({ txid: '0x0cf410cfe7fb268ad06ae115edfa8a30a8dea3979336a647b09b5a789c4b53d5', feeRate: 300 @@ -793,7 +792,7 @@ describe('Wallet', function() { let tempHomeDir: string; let backupDir: string; - + beforeEach(async function () { wallet = new Wallet(ethMigrationTestWalletFixture.wallet as any); tempHomeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'bitcore-client-migration-')); @@ -1173,4 +1172,3 @@ describe('Wallet', function() { }); }); }); - diff --git a/packages/bitcore-node/src/modules/bitcoin-cash/index.ts b/packages/bitcore-node/src/modules/bitcoin-cash/index.ts index 31476283ea7..6c7a5c2a38f 100644 --- a/packages/bitcore-node/src/modules/bitcoin-cash/index.ts +++ b/packages/bitcore-node/src/modules/bitcoin-cash/index.ts @@ -1,15 +1,17 @@ -import { BaseModule } from '..'; +import { ChainStateProvider } from '../../providers/chain-state'; import { BCHStateProvider } from '../../providers/chain-state/bch/bch'; +import { Libs } from '../../providers/libs'; +import { P2P } from '../../services/p2p'; +import { Verification } from '../../services/verification'; import { IUtxoNetworkConfig } from '../../types/Config'; import { VerificationPeer } from '../bitcoin/VerificationPeer'; import { BitcoinP2PWorker } from '../bitcoin/p2p'; -export default class BCHModule extends BaseModule { - constructor(services: BaseModule['bitcoreServices'], chain: string, network: string, _config: IUtxoNetworkConfig) { - super(services); - services.Libs.register(chain, '@bitpay-labs/bitcore-lib-cash', '@bitpay-labs/bitcore-p2p-cash'); - services.P2P.register(chain, network, BitcoinP2PWorker); - services.CSP.registerService(chain, network, new BCHStateProvider()); - services.Verification.register(chain, network, VerificationPeer); +export default class BCHModule { + constructor(chain: string, network: string, _config: IUtxoNetworkConfig) { + Libs.register(chain, '@bitpay-labs/bitcore-lib-cash', '@bitpay-labs/bitcore-p2p-cash'); + P2P.register(chain, network, BitcoinP2PWorker); + ChainStateProvider.registerService(chain, network, new BCHStateProvider()); + Verification.register(chain, network, VerificationPeer); } } diff --git a/packages/bitcore-node/src/modules/bitcoin/index.ts b/packages/bitcore-node/src/modules/bitcoin/index.ts index e5f3b035606..e6dfda72933 100644 --- a/packages/bitcore-node/src/modules/bitcoin/index.ts +++ b/packages/bitcore-node/src/modules/bitcoin/index.ts @@ -1,15 +1,17 @@ -import { BaseModule } from '..'; +import { ChainStateProvider } from '../../providers/chain-state'; import { BTCStateProvider } from '../../providers/chain-state/btc/btc'; +import { Libs } from '../../providers/libs'; +import { P2P } from '../../services/p2p'; +import { Verification } from '../../services/verification'; import { IUtxoNetworkConfig } from '../../types/Config'; import { VerificationPeer } from './VerificationPeer'; import { BitcoinP2PWorker } from './p2p'; -export default class BitcoinModule extends BaseModule { - constructor(services: BaseModule['bitcoreServices'], chain: string, network: string, _config: IUtxoNetworkConfig) { - super(services); - services.Libs.register(chain, '@bitpay-labs/bitcore-lib', '@bitpay-labs/bitcore-p2p'); - services.P2P.register(chain, network, BitcoinP2PWorker); - services.CSP.registerService(chain, network, new BTCStateProvider()); - services.Verification.register(chain, network, VerificationPeer); +export default class BitcoinModule { + constructor(chain: string, network: string, _config: IUtxoNetworkConfig) { + Libs.register(chain, '@bitpay-labs/bitcore-lib', '@bitpay-labs/bitcore-p2p'); + P2P.register(chain, network, BitcoinP2PWorker); + ChainStateProvider.registerService(chain, network, new BTCStateProvider()); + Verification.register(chain, network, VerificationPeer); } } diff --git a/packages/bitcore-node/src/modules/dogecoin/index.ts b/packages/bitcore-node/src/modules/dogecoin/index.ts index 715c513bb47..18da19ec229 100644 --- a/packages/bitcore-node/src/modules/dogecoin/index.ts +++ b/packages/bitcore-node/src/modules/dogecoin/index.ts @@ -1,15 +1,17 @@ -import { BaseModule } from '..'; +import { ChainStateProvider } from '../../providers/chain-state'; import { DOGEStateProvider } from '../../providers/chain-state/doge/doge'; +import { Libs } from '../../providers/libs'; +import { P2P } from '../../services/p2p'; +import { Verification } from '../../services/verification'; import { IUtxoNetworkConfig } from '../../types/Config'; import { VerificationPeer } from '../bitcoin/VerificationPeer'; import { DogecoinP2PWorker } from './p2p'; -export default class DOGEModule extends BaseModule { - constructor(services: BaseModule['bitcoreServices'], chain: string, network: string, _config: IUtxoNetworkConfig) { - super(services); - services.Libs.register(chain, '@bitpay-labs/bitcore-lib-doge', '@bitpay-labs/bitcore-p2p-doge'); - services.P2P.register(chain, network, DogecoinP2PWorker); - services.CSP.registerService(chain, network, new DOGEStateProvider()); - services.Verification.register(chain, network, VerificationPeer); +export default class DOGEModule { + constructor(chain: string, network: string, _config: IUtxoNetworkConfig) { + Libs.register(chain, '@bitpay-labs/bitcore-lib-doge', '@bitpay-labs/bitcore-p2p-doge'); + P2P.register(chain, network, DogecoinP2PWorker); + ChainStateProvider.registerService(chain, network, new DOGEStateProvider()); + Verification.register(chain, network, VerificationPeer); } } diff --git a/packages/bitcore-node/src/modules/ethereum/index.ts b/packages/bitcore-node/src/modules/ethereum/index.ts index 11172ca2c21..42831f7acd3 100644 --- a/packages/bitcore-node/src/modules/ethereum/index.ts +++ b/packages/bitcore-node/src/modules/ethereum/index.ts @@ -1,16 +1,18 @@ -import { BaseModule } from '..'; -import { EVMVerificationPeer } from '../,,/../../providers/chain-state/evm/p2p/EVMVerificationPeer'; +import { ChainStateProvider } from '../../providers/chain-state'; +import { EVMVerificationPeer } from '../../providers/chain-state/evm/p2p/EVMVerificationPeer'; +import { Api } from '../../services/api'; +import { P2P } from '../../services/p2p'; +import { Verification } from '../../services/verification'; import { IEVMNetworkConfig } from '../../types/Config'; import { ETHStateProvider } from './api/csp'; import { EthRoutes } from './api/eth-routes'; import { EthP2pWorker } from './p2p/p2p'; -export default class ETHModule extends BaseModule { - constructor(services: BaseModule['bitcoreServices'], chain: string, network: string, _config: IEVMNetworkConfig) { - super(services); - services.P2P.register(chain, network, EthP2pWorker); - services.CSP.registerService(chain, network, new ETHStateProvider()); - services.Api.app.use(EthRoutes); - services.Verification.register(chain, network, EVMVerificationPeer); +export default class ETHModule { + constructor(chain: string, network: string, _config: IEVMNetworkConfig) { + P2P.register(chain, network, EthP2pWorker); + ChainStateProvider.registerService(chain, network, new ETHStateProvider()); + Api.app.use(EthRoutes); + Verification.register(chain, network, EVMVerificationPeer); } } diff --git a/packages/bitcore-node/src/modules/index.ts b/packages/bitcore-node/src/modules/index.ts index 84b036c8017..f27643669b8 100644 --- a/packages/bitcore-node/src/modules/index.ts +++ b/packages/bitcore-node/src/modules/index.ts @@ -1,53 +1,11 @@ import logger from '../logger'; -import { ChainStateProvider } from '../providers/chain-state'; -import { Libs } from '../providers/libs'; -import { Api } from '../services/api'; import { Config } from '../services/config'; -import { Event } from '../services/event'; -import { P2P } from '../services/p2p'; -import { Storage } from '../services/storage'; -import { Verification } from '../services/verification'; import { ChainNetwork } from '../types/ChainNetwork'; -import { Class } from '../types/Class'; - -export interface IService { - start(): Promise; - stop(): Promise; -} - -export class BaseModule implements IService { - internalServices = new Array(); - constructor( - protected bitcoreServices: { - P2P: typeof P2P; - Storage: typeof Storage; - Event: typeof Event; - Api: typeof Api; - Config: typeof Config; - CSP: typeof ChainStateProvider; - Libs: typeof Libs; - Verification: typeof Verification; - } = { P2P, Storage, Event, Api, Config, CSP: ChainStateProvider, Libs, Verification } - ) {} - - async start() { - for (const service of this.internalServices) { - await service.start(); - } - } - - async stop() { - for (const service of this.internalServices.reverse()) { - await service.stop(); - } - } -} - -class ModuleManager extends BaseModule { - internalServices = new Array(); +export function loadModules(params: Partial = {}) { + const internalServices = new Array(); // Chain names -> module paths map - DEFAULT_MODULE_PATHS = { + const DEFAULT_MODULE_PATHS = { BTC: './bitcoin', ETH: './ethereum', MATIC: './matic', @@ -57,30 +15,25 @@ class ModuleManager extends BaseModule { XRP: './ripple', SOL: './solana' }; - - loadConfigured(params: Partial = {}) { - const chains = params.chain ? [params.chain] : Config.chains(); - - // Auto register known modules from config.chains - for (const chain of chains) { - let modulePath = this.DEFAULT_MODULE_PATHS[chain]; - - // Register for each - const networks = params.network ? [params.network] : Config.networksFor(chain); - for (const network of networks) { - const config = Config.chainConfig({ chain, network }); - modulePath = config.module || modulePath; // custom module path - if (!modulePath) { - logger.warn(`Module not found for ${chain}:${network}. Did you forget to specify 'module' in the config?`); - continue; - } - logger.info(`Registering module for ${chain}:${network}: ${modulePath}`); - // eslint-disable-next-line @typescript-eslint/no-require-imports - const ModuleClass: Class = require(modulePath).default || require(modulePath); - this.internalServices.push(new ModuleClass(this.bitcoreServices, chain, network, config)); + const chains = params.chain ? [params.chain] : Config.chains(); + + // Auto register known modules from config.chains + for (const chain of chains) { + let modulePath = DEFAULT_MODULE_PATHS[chain]; + + // Register for each + const networks = params.network ? [params.network] : Config.networksFor(chain); + for (const network of networks) { + const config = Config.chainConfig({ chain, network }); + modulePath = config.module || modulePath; // custom module path + if (!modulePath) { + logger.warn(`Module not found for ${chain}:${network}. Did you forget to specify 'module' in the config?`); + continue; } + logger.info(`Registering module for ${chain}:${network}: ${modulePath}`); + // eslint-disable-next-line @typescript-eslint/no-require-imports + const ModuleClass = require(modulePath).default || require(modulePath); + internalServices.push(new ModuleClass(chain, network, config)); } } } - -export const Modules = new ModuleManager(); diff --git a/packages/bitcore-node/src/modules/litecoin/index.ts b/packages/bitcore-node/src/modules/litecoin/index.ts index 3dc003f45e9..1953ce9c1d4 100644 --- a/packages/bitcore-node/src/modules/litecoin/index.ts +++ b/packages/bitcore-node/src/modules/litecoin/index.ts @@ -1,15 +1,17 @@ -import { BaseModule } from '..'; +import { ChainStateProvider } from '../../providers/chain-state'; import { LTCStateProvider } from '../../providers/chain-state/ltc/ltc'; +import { Libs } from '../../providers/libs'; +import { P2P } from '../../services/p2p'; +import { Verification } from '../../services/verification'; import { IUtxoNetworkConfig } from '../../types/Config'; import { VerificationPeer } from '../bitcoin/VerificationPeer'; import { LitecoinP2PWorker } from './p2p'; -export default class LTCModule extends BaseModule { - constructor(services: BaseModule['bitcoreServices'], chain: string, network: string, _config: IUtxoNetworkConfig) { - super(services); - services.Libs.register(chain, '@bitpay-labs/bitcore-lib-ltc', '@bitpay-labs/bitcore-p2p'); - services.P2P.register(chain, network, LitecoinP2PWorker); - services.CSP.registerService(chain, network, new LTCStateProvider()); - services.Verification.register(chain, network, VerificationPeer); +export default class LTCModule { + constructor(chain: string, network: string, _config: IUtxoNetworkConfig) { + Libs.register(chain, '@bitpay-labs/bitcore-lib-ltc', '@bitpay-labs/bitcore-p2p'); + P2P.register(chain, network, LitecoinP2PWorker); + ChainStateProvider.registerService(chain, network, new LTCStateProvider()); + Verification.register(chain, network, VerificationPeer); } } diff --git a/packages/bitcore-node/src/modules/matic/index.ts b/packages/bitcore-node/src/modules/matic/index.ts index de76ca035ea..7828cb831e9 100644 --- a/packages/bitcore-node/src/modules/matic/index.ts +++ b/packages/bitcore-node/src/modules/matic/index.ts @@ -1,16 +1,18 @@ -import { BaseModule } from '..'; +import { ChainStateProvider } from '../../providers/chain-state'; import { EVMVerificationPeer } from '../../providers/chain-state/evm/p2p/EVMVerificationPeer'; import { EVMP2pWorker } from '../../providers/chain-state/evm/p2p/p2p'; +import { Api } from '../../services/api'; +import { P2P } from '../../services/p2p'; +import { Verification } from '../../services/verification'; import { IEVMNetworkConfig } from '../../types/Config'; import { MATICStateProvider } from './api/csp'; import { MaticRoutes } from './api/matic-routes'; -export default class MATICModule extends BaseModule { - constructor(services: BaseModule['bitcoreServices'], chain: string, network: string, _config: IEVMNetworkConfig) { - super(services); - services.P2P.register(chain, network, EVMP2pWorker); - services.CSP.registerService(chain, network, new MATICStateProvider()); - services.Api.app.use(MaticRoutes); - services.Verification.register(chain, network, EVMVerificationPeer); +export default class MATICModule { + constructor(chain: string, network: string, _config: IEVMNetworkConfig) { + P2P.register(chain, network, EVMP2pWorker); + ChainStateProvider.registerService(chain, network, new MATICStateProvider()); + Api.app.use(MaticRoutes); + Verification.register(chain, network, EVMVerificationPeer); } } diff --git a/packages/bitcore-node/src/modules/moralis/index.ts b/packages/bitcore-node/src/modules/moralis/index.ts index 8d736473922..30f86388c72 100644 --- a/packages/bitcore-node/src/modules/moralis/index.ts +++ b/packages/bitcore-node/src/modules/moralis/index.ts @@ -1,17 +1,19 @@ -import { BaseModule } from '..'; +import { ChainStateProvider } from '../../providers/chain-state'; import { EVMRouter } from '../../providers/chain-state/evm/api/routes'; import { EVMVerificationPeer } from '../../providers/chain-state/evm/p2p/EVMVerificationPeer'; +import { Api } from '../../services/api'; +import { P2P } from '../../services/p2p'; +import { Verification } from '../../services/verification'; import { IEVMNetworkConfig } from '../../types/Config'; import { MoralisStateProvider } from './api/csp'; import { MoralisP2PWorker } from './p2p/p2p'; -export default class MoralisModule extends BaseModule { - constructor(services: BaseModule['bitcoreServices'], chain: string, network: string, _config: IEVMNetworkConfig) { - super(services); - services.P2P.register(chain, network, MoralisP2PWorker); +export default class MoralisModule { + constructor(chain: string, network: string, _config: IEVMNetworkConfig) { + P2P.register(chain, network, MoralisP2PWorker); const csp = new MoralisStateProvider(chain); - services.CSP.registerService(chain, network, csp); - services.Api.app.use(new EVMRouter(csp, chain).getRouter()); - services.Verification.register(chain, network, EVMVerificationPeer); + ChainStateProvider.registerService(chain, network, csp); + Api.app.use(new EVMRouter(csp, chain).getRouter()); + Verification.register(chain, network, EVMVerificationPeer); } -} \ No newline at end of file +} diff --git a/packages/bitcore-node/src/modules/multiProvider/index.ts b/packages/bitcore-node/src/modules/multiProvider/index.ts index 2640336cde7..1accaac1f7d 100644 --- a/packages/bitcore-node/src/modules/multiProvider/index.ts +++ b/packages/bitcore-node/src/modules/multiProvider/index.ts @@ -1,27 +1,22 @@ -import { BaseModule } from '..'; +import { ChainStateProvider } from '../../providers/chain-state'; import { EVMRouter } from '../../providers/chain-state/evm/api/routes'; import { EVMVerificationPeer } from '../../providers/chain-state/evm/p2p/EVMVerificationPeer'; +import { Api } from '../../services/api'; +import { P2P } from '../../services/p2p'; +import { Verification } from '../../services/verification'; import { MoralisP2PWorker } from '../moralis/p2p/p2p'; import { MultiProviderEVMStateProvider } from './api/csp'; import type { IEVMNetworkConfig } from '../../types/Config'; -export default class MultiProviderModule extends BaseModule { - constructor( - services: BaseModule['bitcoreServices'], - chain: string, - network: string, - _config: IEVMNetworkConfig - ) { - super(services); - +export default class MultiProviderModule { + constructor(chain: string, network: string, _config: IEVMNetworkConfig) { // Reuse MoralisP2PWorker for now (handles webhook sync). // When Alchemy webhooks are added, this can be replaced with a // multi-provider P2P worker. - services.P2P.register(chain, network, MoralisP2PWorker); - + P2P.register(chain, network, MoralisP2PWorker); const csp = new MultiProviderEVMStateProvider(chain); - services.CSP.registerService(chain, network, csp); - services.Api.app.use(new EVMRouter(csp, chain).getRouter()); - services.Verification.register(chain, network, EVMVerificationPeer); + ChainStateProvider.registerService(chain, network, csp); + Api.app.use(new EVMRouter(csp, chain).getRouter()); + Verification.register(chain, network, EVMVerificationPeer); } } diff --git a/packages/bitcore-node/src/modules/ripple/api/event-adapter.ts b/packages/bitcore-node/src/modules/ripple/api/event-adapter.ts index 2b21b0b7d0b..7b4802282ff 100644 --- a/packages/bitcore-node/src/modules/ripple/api/event-adapter.ts +++ b/packages/bitcore-node/src/modules/ripple/api/event-adapter.ts @@ -1,70 +1,12 @@ -import { ObjectId } from 'mongodb'; -import { BaseModule } from '../..'; -import logger from '../../../logger'; -import { RippleStateProvider } from './csp'; import type { XrpRpc } from '@bitpay-labs/crypto-rpc/lib/xrp/XrpRpc'; export class RippleEventAdapter { stopping = false; clients: XrpRpc[] = []; - constructor(protected services: BaseModule['bitcoreServices'], protected network: string) {} + constructor(protected network: string) {} async start() { return; - // TODO: Determine if we still want/need this. - // It's effectively been disabled for a while (maybe forever?) until - // I updated it so the listeners actually get created. However, I don't - // want to blindly start listening for and handling the ledgers & txs - // without thinking through the implications (BWS crash potential, etc). - - this.stopping = false; - logger.info('Starting websocket adapter for Ripple'); - - const networks = this.services.Config.chainNetworks() - .filter(c => c.chain === 'XRP') - .map(c => c.network); - const chain = 'XRP'; - const network = this.network; - const csp = this.services.CSP.get({ chain, network }) as RippleStateProvider; - - for (const network of networks) { - try { - const client = await csp.getClient(network); - - client.rpc.on('ledgerClosed', async ledger => { - if (this.stopping) { - return; - } - this.services.Event.blockEvent.emit('block', { chain, network, ...ledger }); - }); - - client.rpc.on('disconnected', () => { - if (!this.stopping) { - client.rpc.connect(); - } - }); - - client.rpc.on('transaction', async (tx: any) => { // TODO: rm `any` type & fix type errors - if (this.stopping) { - return; - } - const address = tx.transaction.Account; - const transformedTx = { ...csp.transform(tx, network), wallets: new Array() }; - if ('chain' in transformedTx) { - const transformedCoins = csp.transformToCoins(tx, network); - const { transaction, coins } = await csp.tag(chain, network, transformedTx, transformedCoins); - this.services.Event.txEvent.emit('tx', { ...transaction }); - for (const coin of (coins || [])) { - this.services.Event.addressCoinEvent.emit('coin', { address, coin }); - } - } - }); - - await client.asyncRequest('subscribe', { streams: ['ledger', 'transactions_proposed'] }); - } catch (e: any) { - logger.error('Error connecting to XRP: %o', e); - } - } } async stop() { diff --git a/packages/bitcore-node/src/modules/ripple/index.ts b/packages/bitcore-node/src/modules/ripple/index.ts index e74ba261fa6..606e3f1bd7a 100644 --- a/packages/bitcore-node/src/modules/ripple/index.ts +++ b/packages/bitcore-node/src/modules/ripple/index.ts @@ -1,5 +1,9 @@ import { EventEmitter } from 'events'; -import { BaseModule } from '..'; +import { ChainStateProvider } from '../../providers/chain-state'; +import { Api } from '../../services/api'; +import { Event } from '../../services/event'; +import { P2P } from '../../services/p2p'; +import { Verification } from '../../services/verification'; import { IXrpNetworkConfig } from '../../types/Config'; import { RippleStateProvider } from './api/csp'; import { RippleEventAdapter } from './api/event-adapter'; @@ -7,22 +11,21 @@ import { XrpRoutes } from './api/xrp-routes'; import { XrpP2pWorker } from './p2p'; import { XrpVerificationPeer } from './p2p/verification'; -export default class XRPModule extends BaseModule { +export default class XRPModule { static startMonitor: EventEmitter; static endMonitor: EventEmitter; - constructor(services: BaseModule['bitcoreServices'], chain: string, network: string, _config: IXrpNetworkConfig) { - super(services); - services.CSP.registerService(chain, network, new RippleStateProvider()); - services.Api.app.use(XrpRoutes); - services.P2P.register(chain, network, XrpP2pWorker); - services.Verification.register(chain, network, XrpVerificationPeer); + constructor(chain: string, network: string, _config: IXrpNetworkConfig) { + ChainStateProvider.registerService(chain, network, new RippleStateProvider()); + Api.app.use(XrpRoutes); + P2P.register(chain, network, XrpP2pWorker); + Verification.register(chain, network, XrpVerificationPeer); if (!XRPModule.startMonitor) { - const adapter = new RippleEventAdapter(services, network); - XRPModule.startMonitor = services.Event.events.on('start', async () => { + const adapter = new RippleEventAdapter(network); + XRPModule.startMonitor = Event.events.on('start', async () => { await adapter.start(); }); - XRPModule.endMonitor = services.Event.events.on('stop', async () => { + XRPModule.endMonitor = Event.events.on('stop', async () => { await adapter.stop(); }); } diff --git a/packages/bitcore-node/src/modules/solana/index.ts b/packages/bitcore-node/src/modules/solana/index.ts index 1a3f3157788..5928750bff7 100644 --- a/packages/bitcore-node/src/modules/solana/index.ts +++ b/packages/bitcore-node/src/modules/solana/index.ts @@ -1,11 +1,11 @@ -import { BaseModule } from '..'; +import { ChainStateProvider } from '../../providers/chain-state'; +import { Api } from '../../services/api'; import { SOLStateProvider } from './api/csp'; import { SOLRoutes } from './api/sol-routes'; -export default class SOLModule extends BaseModule { - constructor(services: BaseModule['bitcoreServices'], chain: string, network: string) { - super(services); - services.CSP.registerService(chain, network, new SOLStateProvider()); - services.Api.app.use(SOLRoutes); +export default class SOLModule { + constructor(chain: string, network: string) { + ChainStateProvider.registerService(chain, network, new SOLStateProvider()); + Api.app.use(SOLRoutes); } -} \ No newline at end of file +} diff --git a/packages/bitcore-node/src/workers/all.ts b/packages/bitcore-node/src/workers/all.ts index c8ef66e7058..6beead9cbe3 100644 --- a/packages/bitcore-node/src/workers/all.ts +++ b/packages/bitcore-node/src/workers/all.ts @@ -1,7 +1,7 @@ import cluster from 'cluster'; import 'source-map-support/register'; import logger from '../logger'; -import { Modules } from '../modules'; +import { loadModules } from '../modules'; import { Api } from '../services/api'; import { Event } from '../services/event'; import { P2P } from '../services/p2p'; @@ -33,7 +33,7 @@ export const FullClusteredWorker = async () => { services.push(Api); } - Modules.loadConfigured(); + loadModules(); for (const service of services) { await service.start(); diff --git a/packages/bitcore-node/src/workers/api.ts b/packages/bitcore-node/src/workers/api.ts index 049e29660b7..5cff50c30e5 100644 --- a/packages/bitcore-node/src/workers/api.ts +++ b/packages/bitcore-node/src/workers/api.ts @@ -1,7 +1,7 @@ import cluster from 'cluster'; import 'source-map-support/register'; import logger from '../logger'; -import { Modules } from '../modules'; +import { loadModules } from '../modules'; import { Api } from '../services/api'; import { Event } from '../services/event'; import { Storage } from '../services/storage'; @@ -31,7 +31,7 @@ export const ClusteredApiWorker = async () => { services.push(Api); } - Modules.loadConfigured(); + loadModules(); for (const service of services) { await service.start(); @@ -45,7 +45,7 @@ const stop = async () => { process.exit(1); } stopping = true; - + setTimeout(() => { logger.warn('API Worker did not shut down gracefully after 30 seconds, exiting'); process.exit(1); diff --git a/packages/bitcore-node/src/workers/p2p.ts b/packages/bitcore-node/src/workers/p2p.ts index 64354308624..2438a943b32 100644 --- a/packages/bitcore-node/src/workers/p2p.ts +++ b/packages/bitcore-node/src/workers/p2p.ts @@ -1,7 +1,7 @@ import cluster from 'cluster'; import 'source-map-support/register'; import logger from '../logger'; -import { Modules } from '../modules'; +import { loadModules } from '../modules'; import { Config } from '../services/config'; import { Event } from '../services/event'; import { P2P } from '../services/p2p'; @@ -21,7 +21,7 @@ export const P2pWorker = async () => { services.push(Storage, Event); const { CHAIN: chain, NETWORK: network } = process.env; - Modules.loadConfigured({ chain, network }); // loads all if no chain and network specified + loadModules({ chain, network }); // loads all if no chain and network specified // start a particular chain and network, or all of them if (chain && network) { diff --git a/packages/bitcore-node/test/helpers/integration.ts b/packages/bitcore-node/test/helpers/integration.ts index c6b33c05f53..95dffba8beb 100644 --- a/packages/bitcore-node/test/helpers/integration.ts +++ b/packages/bitcore-node/test/helpers/integration.ts @@ -1,5 +1,5 @@ import config from '../../src/config'; -import { Modules } from '../../src/modules'; +import { loadModules } from '../../src/modules'; import { Storage } from '../../src/services/storage'; import { wait } from '../../src/utils'; @@ -12,7 +12,7 @@ let loaded = false; export async function intBeforeHelper() { try { if (!loaded) { - Modules.loadConfigured(); + loadModules(); loaded = true; } if (!Storage.connected) { diff --git a/packages/bitcore-node/test/helpers/unit.ts b/packages/bitcore-node/test/helpers/unit.ts index f8c905a1d7a..65f51e50707 100644 --- a/packages/bitcore-node/test/helpers/unit.ts +++ b/packages/bitcore-node/test/helpers/unit.ts @@ -1,10 +1,10 @@ -import { Modules } from '../../src/modules'; +import { loadModules } from '../../src/modules'; let loaded = false; export async function unitBeforeHelper() { if (!loaded) { console.time('Loading Modules'); - Modules.loadConfigured(); + loadModules(); loaded = true; console.timeEnd('Loading Modules'); } diff --git a/packages/bitcore-node/test/unit/modules.test.ts b/packages/bitcore-node/test/unit/modules.test.ts index ae76d82ce46..c797b7bc882 100644 --- a/packages/bitcore-node/test/unit/modules.test.ts +++ b/packages/bitcore-node/test/unit/modules.test.ts @@ -1,6 +1,8 @@ import { expect } from 'chai'; import sinon from 'sinon'; -import { Modules } from '../../src/modules'; +import { loadModules } from '../../src/modules'; +import { BitcoinP2PWorker } from '../../src/modules/bitcoin/p2p'; +import { EthP2pWorker } from '../../src/modules/ethereum/p2p/p2p'; import { ChainStateProvider } from '../../src/providers/chain-state'; import { Libs } from '../../src/providers/libs'; import { Config } from '../../src/services/config'; @@ -12,7 +14,7 @@ describe('Modules', function() { before(unitBeforeHelper); after(unitAfterHelper); - it('should load configured modules correctly', () => { + it.only('should load configured modules correctly', () => { const sandbox = sinon.createSandbox(); sandbox.stub(Config, 'get').returns(mockConfig); @@ -27,7 +29,7 @@ describe('Modules', function() { sandbox.stub(Config, 'get').returns(mockConfigCopy); try { - Modules.loadConfigured(); + loadModules(); throw new Error('it should have thrown due to a non-existing custom module'); } catch (e: any) { expect(e.message).to.include('Cannot find module \'./bitcoin-custom\''); @@ -108,10 +110,15 @@ const mockConfig = { }; const validateModules = () => { - Modules.internalServices = []; // Remove all loaded modules from internalServices array for a fresh load - Modules.loadConfigured(); // Re-load modules with stubbed Config.get() + const p2pSpy = sinon.spy(P2P, 'register'); - expect(Modules.internalServices.length).to.equal(2); - expect(Modules.internalServices[0].constructor.name).to.equal('BitcoinModule'); - expect(Modules.internalServices[1].constructor.name).to.equal('ETHModule'); + expect(p2pSpy.calledWith('BTC', 'testnet', BitcoinP2PWorker)).to.be.false; + expect(p2pSpy.calledWith('ETH', 'dev', EthP2pWorker)).to.be.false; + expect(p2pSpy.callCount).to.equal(0); + + loadModules(); // Re-load modules with stubbed Config.get() + + expect(p2pSpy.calledWith('BTC', 'testnet', BitcoinP2PWorker)).to.be.true; + expect(p2pSpy.calledWith('ETH', 'dev', EthP2pWorker)).to.be.true; + expect(p2pSpy.callCount).to.equal(2); }; diff --git a/packages/bitcore-node/test/verification/db-repair.ts b/packages/bitcore-node/test/verification/db-repair.ts index 65804b739e3..3c827037fd4 100755 --- a/packages/bitcore-node/test/verification/db-repair.ts +++ b/packages/bitcore-node/test/verification/db-repair.ts @@ -4,7 +4,7 @@ import { Transform } from 'stream'; import { BitcoinBlockStorage } from '../../src/models/block'; import { CoinStorage } from '../../src/models/coin'; import { TransactionStorage } from '../../src/models/transaction'; -import { Modules } from '../../src/modules'; +import { loadModules } from '../../src/modules'; import { Config } from '../../src/services/config'; import { Storage } from '../../src/services/storage'; import { Verification } from '../../src/services/verification'; @@ -19,7 +19,7 @@ import { Verification } from '../../src/services/verification'; const chain = CHAIN || ''; const network = NETWORK || ''; await Storage.start(); - Modules.loadConfigured(); + loadModules(); const chainConfig = Config.chainConfig({ chain, network }); const workerClass = Verification.get(chain, network); diff --git a/packages/bitcore-node/test/verification/db-verify-headers.ts b/packages/bitcore-node/test/verification/db-verify-headers.ts index d4d29afc44c..291f144e97d 100644 --- a/packages/bitcore-node/test/verification/db-verify-headers.ts +++ b/packages/bitcore-node/test/verification/db-verify-headers.ts @@ -2,7 +2,7 @@ import { IBlock } from '../../src/types/Block'; import { BitcoinBlockStorage, IBtcBlock } from '../../src/models/block'; -import { Modules } from '../../src/modules'; +import { loadModules } from '../../src/modules'; import { Storage } from '../../src/services/storage'; if (require.main === module) { @@ -12,7 +12,7 @@ if (require.main === module) { const chain = CHAIN || ''; const network = NETWORK || ''; - Modules.loadConfigured(); + loadModules(); await Storage.start(); let prevMatch = true; let nextMatch = true; @@ -59,7 +59,7 @@ if (require.main === module) { console.log({ block: checkHeight, success }); } } - + process.exit(0); })(); } diff --git a/packages/bitcore-node/test/verification/db-verify.ts b/packages/bitcore-node/test/verification/db-verify.ts index c24f5512517..33c9bd4a18b 100755 --- a/packages/bitcore-node/test/verification/db-verify.ts +++ b/packages/bitcore-node/test/verification/db-verify.ts @@ -1,7 +1,7 @@ #!/usr/bin/env node import { BitcoinBlockStorage } from '../../src/models/block'; -import { Modules } from '../../src/modules'; +import { loadModules } from '../../src/modules'; import { Config } from '../../src/services/config'; import { Storage } from '../../src/services/storage'; import { IVerificationPeer, Verification } from '../../src/services/verification'; @@ -13,7 +13,7 @@ if (require.main === module) { const chain = CHAIN || ''; const network = NETWORK || ''; - Modules.loadConfigured(); + loadModules(); const chainConfig = Config.chainConfig({ chain, network }); let worker: IVerificationPeer; diff --git a/packages/bitcore-node/test/verification/prune-fork.ts b/packages/bitcore-node/test/verification/prune-fork.ts index 8bb4aa781b2..4312003293c 100644 --- a/packages/bitcore-node/test/verification/prune-fork.ts +++ b/packages/bitcore-node/test/verification/prune-fork.ts @@ -2,7 +2,7 @@ import { BitcoinBlockStorage } from '../../src/models/block'; import { CoinStorage } from '../../src/models/coin'; -import { Modules } from '../../src/modules'; +import { loadModules } from '../../src/modules'; import { Config } from '../../src/services/config'; import { Storage } from '../../src/services/storage'; @@ -35,7 +35,7 @@ if (require.main === module) { '<', forkHeight ); - Modules.loadConfigured(); + loadModules(); await Storage.start(); const tip = await BitcoinBlockStorage.getLocalTip({ chain, network }); if (tip) { From 78eb329d11521ba58139186799a6761c56dee8be Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Wed, 6 May 2026 13:47:46 -0400 Subject: [PATCH 2/8] remove internal services and .only from tests --- packages/bitcore-node/src/modules/index.ts | 5 ++--- packages/bitcore-node/test/unit/modules.test.ts | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/bitcore-node/src/modules/index.ts b/packages/bitcore-node/src/modules/index.ts index f27643669b8..70365ff560b 100644 --- a/packages/bitcore-node/src/modules/index.ts +++ b/packages/bitcore-node/src/modules/index.ts @@ -3,7 +3,6 @@ import { Config } from '../services/config'; import { ChainNetwork } from '../types/ChainNetwork'; export function loadModules(params: Partial = {}) { - const internalServices = new Array(); // Chain names -> module paths map const DEFAULT_MODULE_PATHS = { BTC: './bitcoin', @@ -32,8 +31,8 @@ export function loadModules(params: Partial = {}) { } logger.info(`Registering module for ${chain}:${network}: ${modulePath}`); // eslint-disable-next-line @typescript-eslint/no-require-imports - const ModuleClass = require(modulePath).default || require(modulePath); - internalServices.push(new ModuleClass(chain, network, config)); + const Module = require(modulePath).default || require(modulePath); + new Module(chain, network, config); } } } diff --git a/packages/bitcore-node/test/unit/modules.test.ts b/packages/bitcore-node/test/unit/modules.test.ts index c797b7bc882..cde581a72d2 100644 --- a/packages/bitcore-node/test/unit/modules.test.ts +++ b/packages/bitcore-node/test/unit/modules.test.ts @@ -14,7 +14,7 @@ describe('Modules', function() { before(unitBeforeHelper); after(unitAfterHelper); - it.only('should load configured modules correctly', () => { + it('should load configured modules correctly', () => { const sandbox = sinon.createSandbox(); sandbox.stub(Config, 'get').returns(mockConfig); From 73cbda5ed67b087f9385316d789178a77b059c42 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Wed, 6 May 2026 14:26:25 -0400 Subject: [PATCH 3/8] replace module classes with register functions --- .../src/modules/bitcoin-cash/index.ts | 14 ++++----- .../bitcore-node/src/modules/bitcoin/index.ts | 14 ++++----- .../src/modules/dogecoin/index.ts | 14 ++++----- .../src/modules/ethereum/index.ts | 14 ++++----- packages/bitcore-node/src/modules/index.ts | 4 +-- .../src/modules/litecoin/index.ts | 14 ++++----- .../bitcore-node/src/modules/matic/index.ts | 14 ++++----- .../bitcore-node/src/modules/moralis/index.ts | 16 +++++----- .../src/modules/multiProvider/index.ts | 22 +++++++------- .../src/modules/ripple/api/event-adapter.ts | 20 ------------- .../bitcore-node/src/modules/ripple/index.ts | 29 ++++--------------- .../bitcore-node/src/modules/solana/index.ts | 9 +++--- 12 files changed, 65 insertions(+), 119 deletions(-) delete mode 100644 packages/bitcore-node/src/modules/ripple/api/event-adapter.ts diff --git a/packages/bitcore-node/src/modules/bitcoin-cash/index.ts b/packages/bitcore-node/src/modules/bitcoin-cash/index.ts index 6c7a5c2a38f..4d13da291a6 100644 --- a/packages/bitcore-node/src/modules/bitcoin-cash/index.ts +++ b/packages/bitcore-node/src/modules/bitcoin-cash/index.ts @@ -3,15 +3,13 @@ import { BCHStateProvider } from '../../providers/chain-state/bch/bch'; import { Libs } from '../../providers/libs'; import { P2P } from '../../services/p2p'; import { Verification } from '../../services/verification'; -import { IUtxoNetworkConfig } from '../../types/Config'; +import { ChainNetwork } from '../../types/ChainNetwork'; import { VerificationPeer } from '../bitcoin/VerificationPeer'; import { BitcoinP2PWorker } from '../bitcoin/p2p'; -export default class BCHModule { - constructor(chain: string, network: string, _config: IUtxoNetworkConfig) { - Libs.register(chain, '@bitpay-labs/bitcore-lib-cash', '@bitpay-labs/bitcore-p2p-cash'); - P2P.register(chain, network, BitcoinP2PWorker); - ChainStateProvider.registerService(chain, network, new BCHStateProvider()); - Verification.register(chain, network, VerificationPeer); - } +export default function register({ chain, network }: ChainNetwork) { + Libs.register(chain, '@bitpay-labs/bitcore-lib-cash', '@bitpay-labs/bitcore-p2p-cash'); + P2P.register(chain, network, BitcoinP2PWorker); + ChainStateProvider.registerService(chain, network, new BCHStateProvider()); + Verification.register(chain, network, VerificationPeer); } diff --git a/packages/bitcore-node/src/modules/bitcoin/index.ts b/packages/bitcore-node/src/modules/bitcoin/index.ts index e6dfda72933..c44e3642349 100644 --- a/packages/bitcore-node/src/modules/bitcoin/index.ts +++ b/packages/bitcore-node/src/modules/bitcoin/index.ts @@ -3,15 +3,13 @@ import { BTCStateProvider } from '../../providers/chain-state/btc/btc'; import { Libs } from '../../providers/libs'; import { P2P } from '../../services/p2p'; import { Verification } from '../../services/verification'; -import { IUtxoNetworkConfig } from '../../types/Config'; +import { ChainNetwork } from '../../types/ChainNetwork'; import { VerificationPeer } from './VerificationPeer'; import { BitcoinP2PWorker } from './p2p'; -export default class BitcoinModule { - constructor(chain: string, network: string, _config: IUtxoNetworkConfig) { - Libs.register(chain, '@bitpay-labs/bitcore-lib', '@bitpay-labs/bitcore-p2p'); - P2P.register(chain, network, BitcoinP2PWorker); - ChainStateProvider.registerService(chain, network, new BTCStateProvider()); - Verification.register(chain, network, VerificationPeer); - } +export default function register({ chain, network }: ChainNetwork) { + Libs.register(chain, '@bitpay-labs/bitcore-lib', '@bitpay-labs/bitcore-p2p'); + P2P.register(chain, network, BitcoinP2PWorker); + ChainStateProvider.registerService(chain, network, new BTCStateProvider()); + Verification.register(chain, network, VerificationPeer); } diff --git a/packages/bitcore-node/src/modules/dogecoin/index.ts b/packages/bitcore-node/src/modules/dogecoin/index.ts index 18da19ec229..103cd2e9a6d 100644 --- a/packages/bitcore-node/src/modules/dogecoin/index.ts +++ b/packages/bitcore-node/src/modules/dogecoin/index.ts @@ -3,15 +3,13 @@ import { DOGEStateProvider } from '../../providers/chain-state/doge/doge'; import { Libs } from '../../providers/libs'; import { P2P } from '../../services/p2p'; import { Verification } from '../../services/verification'; -import { IUtxoNetworkConfig } from '../../types/Config'; +import { ChainNetwork } from '../../types/ChainNetwork'; import { VerificationPeer } from '../bitcoin/VerificationPeer'; import { DogecoinP2PWorker } from './p2p'; -export default class DOGEModule { - constructor(chain: string, network: string, _config: IUtxoNetworkConfig) { - Libs.register(chain, '@bitpay-labs/bitcore-lib-doge', '@bitpay-labs/bitcore-p2p-doge'); - P2P.register(chain, network, DogecoinP2PWorker); - ChainStateProvider.registerService(chain, network, new DOGEStateProvider()); - Verification.register(chain, network, VerificationPeer); - } +export default function register({ chain, network }: ChainNetwork) { + Libs.register(chain, '@bitpay-labs/bitcore-lib-doge', '@bitpay-labs/bitcore-p2p-doge'); + P2P.register(chain, network, DogecoinP2PWorker); + ChainStateProvider.registerService(chain, network, new DOGEStateProvider()); + Verification.register(chain, network, VerificationPeer); } diff --git a/packages/bitcore-node/src/modules/ethereum/index.ts b/packages/bitcore-node/src/modules/ethereum/index.ts index 42831f7acd3..03182bc1a2e 100644 --- a/packages/bitcore-node/src/modules/ethereum/index.ts +++ b/packages/bitcore-node/src/modules/ethereum/index.ts @@ -3,16 +3,14 @@ import { EVMVerificationPeer } from '../../providers/chain-state/evm/p2p/EVMVeri import { Api } from '../../services/api'; import { P2P } from '../../services/p2p'; import { Verification } from '../../services/verification'; -import { IEVMNetworkConfig } from '../../types/Config'; +import { ChainNetwork } from '../../types/ChainNetwork'; import { ETHStateProvider } from './api/csp'; import { EthRoutes } from './api/eth-routes'; import { EthP2pWorker } from './p2p/p2p'; -export default class ETHModule { - constructor(chain: string, network: string, _config: IEVMNetworkConfig) { - P2P.register(chain, network, EthP2pWorker); - ChainStateProvider.registerService(chain, network, new ETHStateProvider()); - Api.app.use(EthRoutes); - Verification.register(chain, network, EVMVerificationPeer); - } +export default function register({ chain, network }: ChainNetwork) { + P2P.register(chain, network, EthP2pWorker); + ChainStateProvider.registerService(chain, network, new ETHStateProvider()); + Api.app.use(EthRoutes); + Verification.register(chain, network, EVMVerificationPeer); } diff --git a/packages/bitcore-node/src/modules/index.ts b/packages/bitcore-node/src/modules/index.ts index 70365ff560b..bee9c629dee 100644 --- a/packages/bitcore-node/src/modules/index.ts +++ b/packages/bitcore-node/src/modules/index.ts @@ -31,8 +31,8 @@ export function loadModules(params: Partial = {}) { } logger.info(`Registering module for ${chain}:${network}: ${modulePath}`); // eslint-disable-next-line @typescript-eslint/no-require-imports - const Module = require(modulePath).default || require(modulePath); - new Module(chain, network, config); + const register: ({ chain, network }: ChainNetwork) => void = require(modulePath).default; + register({ chain, network }); } } } diff --git a/packages/bitcore-node/src/modules/litecoin/index.ts b/packages/bitcore-node/src/modules/litecoin/index.ts index 1953ce9c1d4..827c4897e88 100644 --- a/packages/bitcore-node/src/modules/litecoin/index.ts +++ b/packages/bitcore-node/src/modules/litecoin/index.ts @@ -3,15 +3,13 @@ import { LTCStateProvider } from '../../providers/chain-state/ltc/ltc'; import { Libs } from '../../providers/libs'; import { P2P } from '../../services/p2p'; import { Verification } from '../../services/verification'; -import { IUtxoNetworkConfig } from '../../types/Config'; +import { ChainNetwork } from '../../types/ChainNetwork'; import { VerificationPeer } from '../bitcoin/VerificationPeer'; import { LitecoinP2PWorker } from './p2p'; -export default class LTCModule { - constructor(chain: string, network: string, _config: IUtxoNetworkConfig) { - Libs.register(chain, '@bitpay-labs/bitcore-lib-ltc', '@bitpay-labs/bitcore-p2p'); - P2P.register(chain, network, LitecoinP2PWorker); - ChainStateProvider.registerService(chain, network, new LTCStateProvider()); - Verification.register(chain, network, VerificationPeer); - } +export default function register({ chain, network }: ChainNetwork) { + Libs.register(chain, '@bitpay-labs/bitcore-lib-ltc', '@bitpay-labs/bitcore-p2p'); + P2P.register(chain, network, LitecoinP2PWorker); + ChainStateProvider.registerService(chain, network, new LTCStateProvider()); + Verification.register(chain, network, VerificationPeer); } diff --git a/packages/bitcore-node/src/modules/matic/index.ts b/packages/bitcore-node/src/modules/matic/index.ts index 7828cb831e9..a1e5bf8ac26 100644 --- a/packages/bitcore-node/src/modules/matic/index.ts +++ b/packages/bitcore-node/src/modules/matic/index.ts @@ -4,15 +4,13 @@ import { EVMP2pWorker } from '../../providers/chain-state/evm/p2p/p2p'; import { Api } from '../../services/api'; import { P2P } from '../../services/p2p'; import { Verification } from '../../services/verification'; -import { IEVMNetworkConfig } from '../../types/Config'; +import { ChainNetwork } from '../../types/ChainNetwork'; import { MATICStateProvider } from './api/csp'; import { MaticRoutes } from './api/matic-routes'; -export default class MATICModule { - constructor(chain: string, network: string, _config: IEVMNetworkConfig) { - P2P.register(chain, network, EVMP2pWorker); - ChainStateProvider.registerService(chain, network, new MATICStateProvider()); - Api.app.use(MaticRoutes); - Verification.register(chain, network, EVMVerificationPeer); - } +export default function register({ chain, network }: ChainNetwork) { + P2P.register(chain, network, EVMP2pWorker); + ChainStateProvider.registerService(chain, network, new MATICStateProvider()); + Api.app.use(MaticRoutes); + Verification.register(chain, network, EVMVerificationPeer); } diff --git a/packages/bitcore-node/src/modules/moralis/index.ts b/packages/bitcore-node/src/modules/moralis/index.ts index 30f86388c72..f6eb76c08c5 100644 --- a/packages/bitcore-node/src/modules/moralis/index.ts +++ b/packages/bitcore-node/src/modules/moralis/index.ts @@ -4,16 +4,14 @@ import { EVMVerificationPeer } from '../../providers/chain-state/evm/p2p/EVMVeri import { Api } from '../../services/api'; import { P2P } from '../../services/p2p'; import { Verification } from '../../services/verification'; -import { IEVMNetworkConfig } from '../../types/Config'; +import { ChainNetwork } from '../../types/ChainNetwork'; import { MoralisStateProvider } from './api/csp'; import { MoralisP2PWorker } from './p2p/p2p'; -export default class MoralisModule { - constructor(chain: string, network: string, _config: IEVMNetworkConfig) { - P2P.register(chain, network, MoralisP2PWorker); - const csp = new MoralisStateProvider(chain); - ChainStateProvider.registerService(chain, network, csp); - Api.app.use(new EVMRouter(csp, chain).getRouter()); - Verification.register(chain, network, EVMVerificationPeer); - } +export default function register({ chain, network }: ChainNetwork) { + P2P.register(chain, network, MoralisP2PWorker); + const csp = new MoralisStateProvider(chain); + ChainStateProvider.registerService(chain, network, csp); + Api.app.use(new EVMRouter(csp, chain).getRouter()); + Verification.register(chain, network, EVMVerificationPeer); } diff --git a/packages/bitcore-node/src/modules/multiProvider/index.ts b/packages/bitcore-node/src/modules/multiProvider/index.ts index 1accaac1f7d..796e5599726 100644 --- a/packages/bitcore-node/src/modules/multiProvider/index.ts +++ b/packages/bitcore-node/src/modules/multiProvider/index.ts @@ -4,19 +4,17 @@ import { EVMVerificationPeer } from '../../providers/chain-state/evm/p2p/EVMVeri import { Api } from '../../services/api'; import { P2P } from '../../services/p2p'; import { Verification } from '../../services/verification'; +import { ChainNetwork } from '../../types/ChainNetwork'; import { MoralisP2PWorker } from '../moralis/p2p/p2p'; import { MultiProviderEVMStateProvider } from './api/csp'; -import type { IEVMNetworkConfig } from '../../types/Config'; -export default class MultiProviderModule { - constructor(chain: string, network: string, _config: IEVMNetworkConfig) { - // Reuse MoralisP2PWorker for now (handles webhook sync). - // When Alchemy webhooks are added, this can be replaced with a - // multi-provider P2P worker. - P2P.register(chain, network, MoralisP2PWorker); - const csp = new MultiProviderEVMStateProvider(chain); - ChainStateProvider.registerService(chain, network, csp); - Api.app.use(new EVMRouter(csp, chain).getRouter()); - Verification.register(chain, network, EVMVerificationPeer); - } +export default function register({ chain, network }: ChainNetwork) { + // Reuse MoralisP2PWorker for now (handles webhook sync). + // When Alchemy webhooks are added, this can be replaced with a + // multi-provider P2P worker. + P2P.register(chain, network, MoralisP2PWorker); + const csp = new MultiProviderEVMStateProvider(chain); + ChainStateProvider.registerService(chain, network, csp); + Api.app.use(new EVMRouter(csp, chain).getRouter()); + Verification.register(chain, network, EVMVerificationPeer); } diff --git a/packages/bitcore-node/src/modules/ripple/api/event-adapter.ts b/packages/bitcore-node/src/modules/ripple/api/event-adapter.ts deleted file mode 100644 index 7b4802282ff..00000000000 --- a/packages/bitcore-node/src/modules/ripple/api/event-adapter.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { XrpRpc } from '@bitpay-labs/crypto-rpc/lib/xrp/XrpRpc'; - -export class RippleEventAdapter { - stopping = false; - clients: XrpRpc[] = []; - constructor(protected network: string) {} - - async start() { - return; - } - - async stop() { - this.stopping = true; - for (const client of this.clients) { - client.rpc.removeAllListeners(); - await client.asyncRequest('unsubscribe', { streams: ['ledger', 'transactions_proposed'] }); - client.rpc.disconnect(); - } - } -} diff --git a/packages/bitcore-node/src/modules/ripple/index.ts b/packages/bitcore-node/src/modules/ripple/index.ts index 606e3f1bd7a..29f3dad600e 100644 --- a/packages/bitcore-node/src/modules/ripple/index.ts +++ b/packages/bitcore-node/src/modules/ripple/index.ts @@ -1,33 +1,16 @@ -import { EventEmitter } from 'events'; import { ChainStateProvider } from '../../providers/chain-state'; import { Api } from '../../services/api'; -import { Event } from '../../services/event'; import { P2P } from '../../services/p2p'; import { Verification } from '../../services/verification'; -import { IXrpNetworkConfig } from '../../types/Config'; +import { ChainNetwork } from '../../types/ChainNetwork'; import { RippleStateProvider } from './api/csp'; -import { RippleEventAdapter } from './api/event-adapter'; import { XrpRoutes } from './api/xrp-routes'; import { XrpP2pWorker } from './p2p'; import { XrpVerificationPeer } from './p2p/verification'; -export default class XRPModule { - static startMonitor: EventEmitter; - static endMonitor: EventEmitter; - constructor(chain: string, network: string, _config: IXrpNetworkConfig) { - ChainStateProvider.registerService(chain, network, new RippleStateProvider()); - Api.app.use(XrpRoutes); - P2P.register(chain, network, XrpP2pWorker); - Verification.register(chain, network, XrpVerificationPeer); - - if (!XRPModule.startMonitor) { - const adapter = new RippleEventAdapter(network); - XRPModule.startMonitor = Event.events.on('start', async () => { - await adapter.start(); - }); - XRPModule.endMonitor = Event.events.on('stop', async () => { - await adapter.stop(); - }); - } - } +export default function register({ chain, network }: ChainNetwork) { + ChainStateProvider.registerService(chain, network, new RippleStateProvider()); + Api.app.use(XrpRoutes); + P2P.register(chain, network, XrpP2pWorker); + Verification.register(chain, network, XrpVerificationPeer); } diff --git a/packages/bitcore-node/src/modules/solana/index.ts b/packages/bitcore-node/src/modules/solana/index.ts index 5928750bff7..f2690328ab9 100644 --- a/packages/bitcore-node/src/modules/solana/index.ts +++ b/packages/bitcore-node/src/modules/solana/index.ts @@ -1,11 +1,10 @@ import { ChainStateProvider } from '../../providers/chain-state'; import { Api } from '../../services/api'; +import { ChainNetwork } from '../../types/ChainNetwork'; import { SOLStateProvider } from './api/csp'; import { SOLRoutes } from './api/sol-routes'; -export default class SOLModule { - constructor(chain: string, network: string) { - ChainStateProvider.registerService(chain, network, new SOLStateProvider()); - Api.app.use(SOLRoutes); - } +export default function register({ chain, network }: ChainNetwork) { + ChainStateProvider.registerService(chain, network, new SOLStateProvider()); + Api.app.use(SOLRoutes); } From 950bba632a11aaf1b49c8dac5af670c5a8df2b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=83=9B=E3=83=83=E3=83=88=E3=83=94=E3=82=B0?= <93409262+MicahMaphet@users.noreply.github.com> Date: Mon, 11 May 2026 12:20:58 -0400 Subject: [PATCH 4/8] accept modules exporting via "module.expports =" Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- packages/bitcore-node/src/modules/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/bitcore-node/src/modules/index.ts b/packages/bitcore-node/src/modules/index.ts index bee9c629dee..938be92fc6b 100644 --- a/packages/bitcore-node/src/modules/index.ts +++ b/packages/bitcore-node/src/modules/index.ts @@ -31,7 +31,11 @@ export function loadModules(params: Partial = {}) { } logger.info(`Registering module for ${chain}:${network}: ${modulePath}`); // eslint-disable-next-line @typescript-eslint/no-require-imports - const register: ({ chain, network }: ChainNetwork) => void = require(modulePath).default; + const loadedModule = require(modulePath); + const register = loadedModule.default ?? loadedModule; + if (typeof register !== 'function') { + throw new TypeError(`Invalid module for ${chain}:${network} at ${modulePath}: expected a default export or module.exports to be a register function`); + } register({ chain, network }); } } From c204ba978c97e15fc4aed60641c6d2828ee4a67e Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Mon, 11 May 2026 13:02:55 -0400 Subject: [PATCH 5/8] use RegisterModule type for modules --- .../bitcore-node/src/modules/bitcoin-cash/index.ts | 8 +++++--- packages/bitcore-node/src/modules/bitcoin/index.ts | 8 +++++--- packages/bitcore-node/src/modules/dogecoin/index.ts | 8 +++++--- packages/bitcore-node/src/modules/ethereum/index.ts | 8 +++++--- packages/bitcore-node/src/modules/litecoin/index.ts | 8 +++++--- packages/bitcore-node/src/modules/matic/index.ts | 8 +++++--- packages/bitcore-node/src/modules/modules.md | 13 ++++++------- packages/bitcore-node/src/modules/moralis/index.ts | 8 +++++--- .../bitcore-node/src/modules/multiProvider/index.ts | 8 +++++--- packages/bitcore-node/src/modules/ripple/index.ts | 8 +++++--- packages/bitcore-node/src/modules/solana/index.ts | 9 ++++++--- packages/bitcore-node/src/types/Module.ts | 3 +++ 12 files changed, 60 insertions(+), 37 deletions(-) create mode 100644 packages/bitcore-node/src/types/Module.ts diff --git a/packages/bitcore-node/src/modules/bitcoin-cash/index.ts b/packages/bitcore-node/src/modules/bitcoin-cash/index.ts index 4d13da291a6..6aeefa9ede1 100644 --- a/packages/bitcore-node/src/modules/bitcoin-cash/index.ts +++ b/packages/bitcore-node/src/modules/bitcoin-cash/index.ts @@ -3,13 +3,15 @@ import { BCHStateProvider } from '../../providers/chain-state/bch/bch'; import { Libs } from '../../providers/libs'; import { P2P } from '../../services/p2p'; import { Verification } from '../../services/verification'; -import { ChainNetwork } from '../../types/ChainNetwork'; +import { RegisterModule } from '../../types/Module'; import { VerificationPeer } from '../bitcoin/VerificationPeer'; import { BitcoinP2PWorker } from '../bitcoin/p2p'; -export default function register({ chain, network }: ChainNetwork) { +const registerModule: RegisterModule = ({ chain, network }) => { Libs.register(chain, '@bitpay-labs/bitcore-lib-cash', '@bitpay-labs/bitcore-p2p-cash'); P2P.register(chain, network, BitcoinP2PWorker); ChainStateProvider.registerService(chain, network, new BCHStateProvider()); Verification.register(chain, network, VerificationPeer); -} +}; + +export default registerModule; diff --git a/packages/bitcore-node/src/modules/bitcoin/index.ts b/packages/bitcore-node/src/modules/bitcoin/index.ts index c44e3642349..59588c3bc5d 100644 --- a/packages/bitcore-node/src/modules/bitcoin/index.ts +++ b/packages/bitcore-node/src/modules/bitcoin/index.ts @@ -3,13 +3,15 @@ import { BTCStateProvider } from '../../providers/chain-state/btc/btc'; import { Libs } from '../../providers/libs'; import { P2P } from '../../services/p2p'; import { Verification } from '../../services/verification'; -import { ChainNetwork } from '../../types/ChainNetwork'; +import { RegisterModule } from '../../types/Module'; import { VerificationPeer } from './VerificationPeer'; import { BitcoinP2PWorker } from './p2p'; -export default function register({ chain, network }: ChainNetwork) { +const registerModule: RegisterModule = ({ chain, network }) => { Libs.register(chain, '@bitpay-labs/bitcore-lib', '@bitpay-labs/bitcore-p2p'); P2P.register(chain, network, BitcoinP2PWorker); ChainStateProvider.registerService(chain, network, new BTCStateProvider()); Verification.register(chain, network, VerificationPeer); -} +}; + +export default registerModule; diff --git a/packages/bitcore-node/src/modules/dogecoin/index.ts b/packages/bitcore-node/src/modules/dogecoin/index.ts index 103cd2e9a6d..05aad6e321e 100644 --- a/packages/bitcore-node/src/modules/dogecoin/index.ts +++ b/packages/bitcore-node/src/modules/dogecoin/index.ts @@ -3,13 +3,15 @@ import { DOGEStateProvider } from '../../providers/chain-state/doge/doge'; import { Libs } from '../../providers/libs'; import { P2P } from '../../services/p2p'; import { Verification } from '../../services/verification'; -import { ChainNetwork } from '../../types/ChainNetwork'; +import { RegisterModule } from '../../types/Module'; import { VerificationPeer } from '../bitcoin/VerificationPeer'; import { DogecoinP2PWorker } from './p2p'; -export default function register({ chain, network }: ChainNetwork) { +const registerModule: RegisterModule = ({ chain, network }) => { Libs.register(chain, '@bitpay-labs/bitcore-lib-doge', '@bitpay-labs/bitcore-p2p-doge'); P2P.register(chain, network, DogecoinP2PWorker); ChainStateProvider.registerService(chain, network, new DOGEStateProvider()); Verification.register(chain, network, VerificationPeer); -} +}; + +export default registerModule; diff --git a/packages/bitcore-node/src/modules/ethereum/index.ts b/packages/bitcore-node/src/modules/ethereum/index.ts index 03182bc1a2e..000f1aa89c7 100644 --- a/packages/bitcore-node/src/modules/ethereum/index.ts +++ b/packages/bitcore-node/src/modules/ethereum/index.ts @@ -3,14 +3,16 @@ import { EVMVerificationPeer } from '../../providers/chain-state/evm/p2p/EVMVeri import { Api } from '../../services/api'; import { P2P } from '../../services/p2p'; import { Verification } from '../../services/verification'; -import { ChainNetwork } from '../../types/ChainNetwork'; +import { RegisterModule } from '../../types/Module'; import { ETHStateProvider } from './api/csp'; import { EthRoutes } from './api/eth-routes'; import { EthP2pWorker } from './p2p/p2p'; -export default function register({ chain, network }: ChainNetwork) { +const registerModule: RegisterModule = ({ chain, network }) => { P2P.register(chain, network, EthP2pWorker); ChainStateProvider.registerService(chain, network, new ETHStateProvider()); Api.app.use(EthRoutes); Verification.register(chain, network, EVMVerificationPeer); -} +}; + +export default registerModule; diff --git a/packages/bitcore-node/src/modules/litecoin/index.ts b/packages/bitcore-node/src/modules/litecoin/index.ts index 827c4897e88..ee58d316848 100644 --- a/packages/bitcore-node/src/modules/litecoin/index.ts +++ b/packages/bitcore-node/src/modules/litecoin/index.ts @@ -3,13 +3,15 @@ import { LTCStateProvider } from '../../providers/chain-state/ltc/ltc'; import { Libs } from '../../providers/libs'; import { P2P } from '../../services/p2p'; import { Verification } from '../../services/verification'; -import { ChainNetwork } from '../../types/ChainNetwork'; +import { RegisterModule } from '../../types/Module'; import { VerificationPeer } from '../bitcoin/VerificationPeer'; import { LitecoinP2PWorker } from './p2p'; -export default function register({ chain, network }: ChainNetwork) { +const registerModule: RegisterModule = ({ chain, network }) => { Libs.register(chain, '@bitpay-labs/bitcore-lib-ltc', '@bitpay-labs/bitcore-p2p'); P2P.register(chain, network, LitecoinP2PWorker); ChainStateProvider.registerService(chain, network, new LTCStateProvider()); Verification.register(chain, network, VerificationPeer); -} +}; + +export default registerModule; diff --git a/packages/bitcore-node/src/modules/matic/index.ts b/packages/bitcore-node/src/modules/matic/index.ts index a1e5bf8ac26..f32209287e9 100644 --- a/packages/bitcore-node/src/modules/matic/index.ts +++ b/packages/bitcore-node/src/modules/matic/index.ts @@ -4,13 +4,15 @@ import { EVMP2pWorker } from '../../providers/chain-state/evm/p2p/p2p'; import { Api } from '../../services/api'; import { P2P } from '../../services/p2p'; import { Verification } from '../../services/verification'; -import { ChainNetwork } from '../../types/ChainNetwork'; +import { RegisterModule } from '../../types/Module'; import { MATICStateProvider } from './api/csp'; import { MaticRoutes } from './api/matic-routes'; -export default function register({ chain, network }: ChainNetwork) { +const registerModule: RegisterModule = ({ chain, network }) => { P2P.register(chain, network, EVMP2pWorker); ChainStateProvider.registerService(chain, network, new MATICStateProvider()); Api.app.use(MaticRoutes); Verification.register(chain, network, EVMVerificationPeer); -} +}; + +export default registerModule; diff --git a/packages/bitcore-node/src/modules/modules.md b/packages/bitcore-node/src/modules/modules.md index f7563c55748..ec375ae6c80 100644 --- a/packages/bitcore-node/src/modules/modules.md +++ b/packages/bitcore-node/src/modules/modules.md @@ -24,13 +24,12 @@ Let's say we have a node_module, named `bitcore-node-bch` with the following cod ``` // index.js -module.exports = class BitcoinCashModule { - constructor(services, chain, network, config) { - // chain === 'BCH' - services.Libs.register(chain, '@bitpay-labs/bitcore-lib-cash', '@bitpay-labs/bitcore-p2p-cash'); - services.P2P.register(chain, network, services.P2P.get('BTC')); - } +const registerModule: RegisterModule = (chain, network) => { + // chain === 'BCH' + Libs.register(chain, '@bitpay-labs/bitcore-lib-cash', '@bitpay-labs/bitcore-p2p-cash'); + P2P.register(chain, network, P2P.get('BTC')); } +module.exports.default = registerModule ``` The module has the following dependencies @@ -76,4 +75,4 @@ For example, `moralis` is a module for [Moralis.io](https://moralis.io), a block } } } -``` \ No newline at end of file +``` diff --git a/packages/bitcore-node/src/modules/moralis/index.ts b/packages/bitcore-node/src/modules/moralis/index.ts index f6eb76c08c5..b4f03e55b0c 100644 --- a/packages/bitcore-node/src/modules/moralis/index.ts +++ b/packages/bitcore-node/src/modules/moralis/index.ts @@ -4,14 +4,16 @@ import { EVMVerificationPeer } from '../../providers/chain-state/evm/p2p/EVMVeri import { Api } from '../../services/api'; import { P2P } from '../../services/p2p'; import { Verification } from '../../services/verification'; -import { ChainNetwork } from '../../types/ChainNetwork'; +import { RegisterModule } from '../../types/Module'; import { MoralisStateProvider } from './api/csp'; import { MoralisP2PWorker } from './p2p/p2p'; -export default function register({ chain, network }: ChainNetwork) { +const registerModule: RegisterModule = ({ chain, network }) => { P2P.register(chain, network, MoralisP2PWorker); const csp = new MoralisStateProvider(chain); ChainStateProvider.registerService(chain, network, csp); Api.app.use(new EVMRouter(csp, chain).getRouter()); Verification.register(chain, network, EVMVerificationPeer); -} +}; + +export default registerModule; diff --git a/packages/bitcore-node/src/modules/multiProvider/index.ts b/packages/bitcore-node/src/modules/multiProvider/index.ts index 796e5599726..54dcaa6f173 100644 --- a/packages/bitcore-node/src/modules/multiProvider/index.ts +++ b/packages/bitcore-node/src/modules/multiProvider/index.ts @@ -4,11 +4,11 @@ import { EVMVerificationPeer } from '../../providers/chain-state/evm/p2p/EVMVeri import { Api } from '../../services/api'; import { P2P } from '../../services/p2p'; import { Verification } from '../../services/verification'; -import { ChainNetwork } from '../../types/ChainNetwork'; +import { RegisterModule } from '../../types/Module'; import { MoralisP2PWorker } from '../moralis/p2p/p2p'; import { MultiProviderEVMStateProvider } from './api/csp'; -export default function register({ chain, network }: ChainNetwork) { +const registerModule: RegisterModule = ({ chain, network }) => { // Reuse MoralisP2PWorker for now (handles webhook sync). // When Alchemy webhooks are added, this can be replaced with a // multi-provider P2P worker. @@ -17,4 +17,6 @@ export default function register({ chain, network }: ChainNetwork) { ChainStateProvider.registerService(chain, network, csp); Api.app.use(new EVMRouter(csp, chain).getRouter()); Verification.register(chain, network, EVMVerificationPeer); -} +}; + +export default registerModule; diff --git a/packages/bitcore-node/src/modules/ripple/index.ts b/packages/bitcore-node/src/modules/ripple/index.ts index 29f3dad600e..024b76e6d4c 100644 --- a/packages/bitcore-node/src/modules/ripple/index.ts +++ b/packages/bitcore-node/src/modules/ripple/index.ts @@ -2,15 +2,17 @@ import { ChainStateProvider } from '../../providers/chain-state'; import { Api } from '../../services/api'; import { P2P } from '../../services/p2p'; import { Verification } from '../../services/verification'; -import { ChainNetwork } from '../../types/ChainNetwork'; +import { RegisterModule } from '../../types/Module'; import { RippleStateProvider } from './api/csp'; import { XrpRoutes } from './api/xrp-routes'; import { XrpP2pWorker } from './p2p'; import { XrpVerificationPeer } from './p2p/verification'; -export default function register({ chain, network }: ChainNetwork) { +const registerModule: RegisterModule = ({ chain, network }) => { ChainStateProvider.registerService(chain, network, new RippleStateProvider()); Api.app.use(XrpRoutes); P2P.register(chain, network, XrpP2pWorker); Verification.register(chain, network, XrpVerificationPeer); -} +}; + +export default registerModule; diff --git a/packages/bitcore-node/src/modules/solana/index.ts b/packages/bitcore-node/src/modules/solana/index.ts index f2690328ab9..58d5cf1407a 100644 --- a/packages/bitcore-node/src/modules/solana/index.ts +++ b/packages/bitcore-node/src/modules/solana/index.ts @@ -1,10 +1,13 @@ import { ChainStateProvider } from '../../providers/chain-state'; import { Api } from '../../services/api'; -import { ChainNetwork } from '../../types/ChainNetwork'; +import { RegisterModule } from '../../types/Module'; import { SOLStateProvider } from './api/csp'; import { SOLRoutes } from './api/sol-routes'; -export default function register({ chain, network }: ChainNetwork) { + +const registerModule: RegisterModule = ({ chain, network }) => { ChainStateProvider.registerService(chain, network, new SOLStateProvider()); Api.app.use(SOLRoutes); -} +}; + +export default registerModule; \ No newline at end of file diff --git a/packages/bitcore-node/src/types/Module.ts b/packages/bitcore-node/src/types/Module.ts new file mode 100644 index 00000000000..628d275dffc --- /dev/null +++ b/packages/bitcore-node/src/types/Module.ts @@ -0,0 +1,3 @@ +import { ChainNetwork } from './ChainNetwork'; + +export type RegisterModule = (params: ChainNetwork) => void; \ No newline at end of file From 40ae54c736a9247afd60e68ac2037d1514ace5b0 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Mon, 11 May 2026 13:08:36 -0400 Subject: [PATCH 6/8] fix module path overwrite bug --- packages/bitcore-node/src/modules/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/bitcore-node/src/modules/index.ts b/packages/bitcore-node/src/modules/index.ts index 938be92fc6b..6b6b04e2517 100644 --- a/packages/bitcore-node/src/modules/index.ts +++ b/packages/bitcore-node/src/modules/index.ts @@ -18,13 +18,13 @@ export function loadModules(params: Partial = {}) { // Auto register known modules from config.chains for (const chain of chains) { - let modulePath = DEFAULT_MODULE_PATHS[chain]; + const defaultModulePath = DEFAULT_MODULE_PATHS[chain]; // Register for each const networks = params.network ? [params.network] : Config.networksFor(chain); for (const network of networks) { const config = Config.chainConfig({ chain, network }); - modulePath = config.module || modulePath; // custom module path + const modulePath = config.module || defaultModulePath; // custom module path if (!modulePath) { logger.warn(`Module not found for ${chain}:${network}. Did you forget to specify 'module' in the config?`); continue; From 05254ad9cb4ed5ec84e65bc9fca00a09b2b0acbd Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Mon, 11 May 2026 13:11:49 -0400 Subject: [PATCH 7/8] restore spy for modules unit test --- packages/bitcore-node/test/unit/modules.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/bitcore-node/test/unit/modules.test.ts b/packages/bitcore-node/test/unit/modules.test.ts index cde581a72d2..19183735b63 100644 --- a/packages/bitcore-node/test/unit/modules.test.ts +++ b/packages/bitcore-node/test/unit/modules.test.ts @@ -121,4 +121,6 @@ const validateModules = () => { expect(p2pSpy.calledWith('BTC', 'testnet', BitcoinP2PWorker)).to.be.true; expect(p2pSpy.calledWith('ETH', 'dev', EthP2pWorker)).to.be.true; expect(p2pSpy.callCount).to.equal(2); + + p2pSpy.restore(); }; From c2440356ebc91026341dfb2f293a8235435003dc Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Mon, 11 May 2026 13:14:58 -0400 Subject: [PATCH 8/8] wrap in try finally to ensure the spy restores --- .../bitcore-node/test/unit/modules.test.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/bitcore-node/test/unit/modules.test.ts b/packages/bitcore-node/test/unit/modules.test.ts index 19183735b63..71855d7254a 100644 --- a/packages/bitcore-node/test/unit/modules.test.ts +++ b/packages/bitcore-node/test/unit/modules.test.ts @@ -111,16 +111,17 @@ const mockConfig = { const validateModules = () => { const p2pSpy = sinon.spy(P2P, 'register'); + try { + expect(p2pSpy.calledWith('BTC', 'testnet', BitcoinP2PWorker)).to.be.false; + expect(p2pSpy.calledWith('ETH', 'dev', EthP2pWorker)).to.be.false; + expect(p2pSpy.callCount).to.equal(0); - expect(p2pSpy.calledWith('BTC', 'testnet', BitcoinP2PWorker)).to.be.false; - expect(p2pSpy.calledWith('ETH', 'dev', EthP2pWorker)).to.be.false; - expect(p2pSpy.callCount).to.equal(0); + loadModules(); // Re-load modules with stubbed Config.get() - loadModules(); // Re-load modules with stubbed Config.get() - - expect(p2pSpy.calledWith('BTC', 'testnet', BitcoinP2PWorker)).to.be.true; - expect(p2pSpy.calledWith('ETH', 'dev', EthP2pWorker)).to.be.true; - expect(p2pSpy.callCount).to.equal(2); - - p2pSpy.restore(); + expect(p2pSpy.calledWith('BTC', 'testnet', BitcoinP2PWorker)).to.be.true; + expect(p2pSpy.calledWith('ETH', 'dev', EthP2pWorker)).to.be.true; + expect(p2pSpy.callCount).to.equal(2); + } finally { + p2pSpy.restore(); + } };