From c0ae808793237c0c0b36bfaeea88cce4b59d9c5e Mon Sep 17 00:00:00 2001 From: David Klakurka Date: Sun, 21 Dec 2025 14:47:19 -0800 Subject: [PATCH 1/2] Upgraded to latest chronik-client that supports UNKNOWN token types --- package.json | 5 +- services/chronikService.ts | 2 +- tests/unittests/chronikService.test.ts | 150 +++++++++--------- .../handleUpdateClientPaymentStatus.test.ts | 2 +- 4 files changed, 76 insertions(+), 83 deletions(-) diff --git a/package.json b/package.json index 7eacc7f7..4a37acd8 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "bitcoinjs-lib": "^6.0.2", "bs58": "^5.0.0", "chart.js": "^3.8.0", - "chronik-client-cashtokens": "^3.1.1-rc0", + "chronik-client": "^4.1.0", "cors": "^2.8.5", "cross-env": "^7.0.2", "dotenv-cli": "^5.1.0", @@ -97,9 +97,6 @@ "tsx": "^3.12.1", "typescript": "^5.7.2" }, - "resolutions": { - "chronik-client-cashtokens/ecashaddrjs": "^2.0.0" - }, "lint-staged": { "*.{ts,tsx}": [ "node ./node_modules/eslint/bin/eslint.js --fix" diff --git a/services/chronikService.ts b/services/chronikService.ts index 56e3d3fa..1a80d849 100644 --- a/services/chronikService.ts +++ b/services/chronikService.ts @@ -1,4 +1,4 @@ -import { BlockInfo, ChronikClient, ConnectionStrategy, ScriptUtxo, Tx, WsConfig, WsEndpoint, WsMsgClient, WsSubScriptClient } from 'chronik-client-cashtokens' +import { BlockInfo, ChronikClient, ConnectionStrategy, ScriptUtxo, Tx, WsConfig, WsEndpoint, WsMsgClient, WsSubScriptClient } from 'chronik-client' import { encodeCashAddress, decodeCashAddress } from 'ecashaddrjs' import { AddressWithTransaction, BlockchainInfo, TransactionDetails, ProcessedMessages, SubbedAddressesLog, SyncAndSubscriptionReturn, SubscriptionReturn, SimpleBlockInfo } from 'types/chronikTypes' import { CHRONIK_MESSAGE_CACHE_DELAY, RESPONSE_MESSAGES, XEC_TIMESTAMP_THRESHOLD, XEC_NETWORK_ID, BCH_NETWORK_ID, BCH_TIMESTAMP_THRESHOLD, CHRONIK_FETCH_N_TXS_PER_PAGE, KeyValueT, NETWORK_IDS_FROM_SLUGS, SOCKET_MESSAGES, NETWORK_IDS, NETWORK_TICKERS, MainNetworkSlugsType, MAX_MEMPOOL_TXS_TO_PROCESS_AT_A_TIME, MEMPOOL_PROCESS_DELAY, CHRONIK_INITIALIZATION_DELAY, LATENCY_TEST_CHECK_DELAY, INITIAL_ADDRESS_SYNC_FETCH_CONCURRENTLY, TX_EMIT_BATCH_SIZE, DB_COMMIT_BATCH_SIZE } from 'constants/index' diff --git a/tests/unittests/chronikService.test.ts b/tests/unittests/chronikService.test.ts index 016e2901..18de3f67 100644 --- a/tests/unittests/chronikService.test.ts +++ b/tests/unittests/chronikService.test.ts @@ -1,8 +1,6 @@ // Set up environment variables BEFORE any imports -process.env.WS_AUTH_KEY = 'test-auth-key' - import { EMPTY_OP_RETURN } from 'utils/validators' -import { +import { getNullDataScriptData, fromHash160, toHash160, @@ -11,9 +9,14 @@ import { multiBlockchainClient } from '../../services/chronikService' import { Address } from '@prisma/client' +import { fetchAddressesArray } from '../../services/addressService' +import { fetchUnconfirmedTransactions, deleteTransactions, upsertTransaction } from '../../services/transactionService' +import { executeAddressTriggers } from '../../services/triggerService' + +process.env.WS_AUTH_KEY = 'test-auth-key' // Mock the heavy dependencies to avoid network calls in tests -jest.mock('chronik-client-cashtokens', () => ({ +jest.mock('chronik-client', () => ({ ChronikClient: { useStrategy: jest.fn().mockResolvedValue({ ws: jest.fn().mockReturnValue({ @@ -21,7 +24,7 @@ jest.mock('chronik-client-cashtokens', () => ({ subscribeToBlocks: jest.fn(), subs: { scripts: [] } }), - tx: jest.fn(), // <-- needed + tx: jest.fn() // <-- needed }) }, ConnectionStrategy: { @@ -266,7 +269,7 @@ describe('toHash160 tests', () => { const originalHash160 = 'c5d2460186f7233c927e7db2dcc703c0e500b653' // 40 chars const address = fromHash160('ecash', 'p2pkh', originalHash160) const result = toHash160(address) - + // The hash160 should convert back to the same address const addressAgain = fromHash160('ecash', result.type, result.hash160) expect(addressAgain).toBe(address) @@ -569,11 +572,11 @@ describe('Additional coverage tests', () => { it('Should handle toHash160 error logging', () => { const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {}) - + expect(() => { toHash160('definitely-invalid-address-format') }).toThrow() - + expect(consoleSpy).toHaveBeenCalledWith('[CHRONIK]: Error converting address to hash160') consoleSpy.mockRestore() }) @@ -625,11 +628,11 @@ describe('ChronikBlockchainClient methods coverage', () => { scripts: [ { scriptType: 'p2pkh', - payload: 'c5d2460186f7233c927e7db2dcc703c0e500b653' // 40 chars (20 bytes) + payload: 'c5d2460186f7233c927e7db2dcc703c0e500b653' // 40 chars (20 bytes) }, { - scriptType: 'p2sh', - payload: 'd5e2470186f7233c927e7db2dcc703c0e500b653' // 40 chars (20 bytes) + scriptType: 'p2sh', + payload: 'd5e2470186f7233c927e7db2dcc703c0e500b653' // 40 chars (20 bytes) } ] } @@ -813,7 +816,7 @@ describe('ChronikBlockchainClient advanced functionality', () => { const mockSubscribeToAddress = jest.fn().mockImplementation(() => { throw new Error('Subscription failed') }) - + client.chronikWSEndpoint = { subscribeToAddress: mockSubscribeToAddress, subs: { scripts: [] } @@ -824,10 +827,10 @@ describe('ChronikBlockchainClient advanced functionality', () => { const result = await client.subscribeAddresses(mockAddresses) expect(result).toHaveProperty('failedAddressesWithErrors') - + // The mock function should have been called since the address passed the filters expect(mockSubscribeToAddress).toHaveBeenCalledWith(validAddress) - + // And there should be an error recorded expect(Object.keys(result.failedAddressesWithErrors)).toHaveLength(1) expect(result.failedAddressesWithErrors[validAddress]).toContain('Error: Subscription failed') @@ -836,7 +839,7 @@ describe('ChronikBlockchainClient advanced functionality', () => { it('Should filter addresses by network ID', async () => { const validXecAddress = fromHash160('ecash', 'p2pkh', 'c5d2460186f7233c927e7db2dcc703c0e500b653') const validBchAddress = fromHash160('bitcoincash', 'p2pkh', 'd5e2470186f7233c927e7db2dcc703c0e500b653') - + const mockAddresses = [ { id: '1', @@ -866,15 +869,15 @@ describe('ChronikBlockchainClient advanced functionality', () => { // Mock getSubscribedAddresses to return empty array so addresses are not filtered out jest.spyOn(client, 'getSubscribedAddresses').mockReturnValue([]) - const result = await client.subscribeAddresses(mockAddresses) - + await client.subscribeAddresses(mockAddresses) + // Only the XEC address should be processed (the BCH address should be filtered out by network ID) expect(client.chronikWSEndpoint.subscribeToAddress).toHaveBeenCalledTimes(1) expect(client.chronikWSEndpoint.subscribeToAddress).toHaveBeenCalledWith(validXecAddress) }) it('Should handle already subscribed addresses', async () => { - // Use a valid address generated from a known hash160 + // Use a valid address generated from a known hash160 const validAddress = fromHash160('ecash', 'p2pkh', 'c5d2460186f7233c927e7db2dcc703c0e500b653') const mockAddresses = [ { @@ -891,7 +894,7 @@ describe('ChronikBlockchainClient advanced functionality', () => { // Mock already subscribed addresses client.chronikWSEndpoint = { subscribeToAddress: jest.fn(), - subs: { + subs: { scripts: [ { scriptType: 'p2pkh', @@ -905,7 +908,7 @@ describe('ChronikBlockchainClient advanced functionality', () => { jest.spyOn(client, 'getSubscribedAddresses').mockReturnValue([validAddress]) const result = await client.subscribeAddresses(mockAddresses) - + // Should not try to subscribe again expect(client.chronikWSEndpoint.subscribeToAddress).not.toHaveBeenCalled() expect(Object.keys(result.failedAddressesWithErrors)).toHaveLength(0) @@ -926,9 +929,8 @@ describe('ChronikBlockchainClient advanced functionality', () => { // Mock the sync generator method const mockSyncGenerator = { - async *syncTransactionsForAddress(address: Address) { + async * syncTransactionsForAddress (address: Address) { yield [] - return } } @@ -962,7 +964,7 @@ describe('ChronikBlockchainClient advanced functionality', () => { it('Should handle network validation error', async () => { const bitcoincashClient = new ChronikBlockchainClient('bitcoincash') - + // Try to get blockchain info for wrong network await expect(bitcoincashClient.getBlockchainInfo('ecash')).rejects.toThrow() }) @@ -970,21 +972,21 @@ describe('ChronikBlockchainClient advanced functionality', () => { it('Should handle errors in sync missed transactions', async () => { const client = new ChronikBlockchainClient('ecash') const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}) - + // Mock syncAddresses method to throw an error, which will be caught by the try-catch const syncAddressesSpy = jest.spyOn(client, 'syncAddresses').mockRejectedValue(new Error('Sync failed')) // The method catches errors and logs them, so it should resolve (not reject) await client.syncMissedTransactions() - + // Verify that syncAddresses was called expect(syncAddressesSpy).toHaveBeenCalled() - + // Verify that the error was logged expect(consoleSpy).toHaveBeenCalledWith( expect.stringContaining('[CHRONIK — ecash]: ERROR: (skipping anyway) initial missing transactions sync failed: Sync failed') ) - + consoleSpy.mockRestore() syncAddressesSpy.mockRestore() }) @@ -992,21 +994,21 @@ describe('ChronikBlockchainClient advanced functionality', () => { it('Should handle errors in subscribe initial addresses', async () => { const client = new ChronikBlockchainClient('ecash') const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}) - + // Mock subscribeAddresses method to throw an error, which will be caught by the try-catch const subscribeAddressesSpy = jest.spyOn(client, 'subscribeAddresses').mockRejectedValue(new Error('Subscribe failed')) // The method catches errors and logs them, so it should resolve (not reject) await client.subscribeInitialAddresses() - + // Verify that subscribeAddresses was called expect(subscribeAddressesSpy).toHaveBeenCalled() - + // Verify that the error was logged expect(consoleSpy).toHaveBeenCalledWith( expect.stringContaining('[CHRONIK — ecash]: ERROR: (skipping anyway) initial chronik subscription failed: Subscribe failed') ) - + consoleSpy.mockRestore() subscribeAddressesSpy.mockRestore() }) @@ -1022,7 +1024,7 @@ describe('MultiBlockchainClient method coverage', () => { // The multiBlockchainClient should already be initialized or initializing // Mock the waitForStart to avoid actual initialization jest.spyOn(multiBlockchainClient, 'waitForStart').mockResolvedValue() - + await multiBlockchainClient.waitForStart() expect(multiBlockchainClient.waitForStart).toHaveBeenCalled() }) @@ -1033,9 +1035,9 @@ describe('MultiBlockchainClient method coverage', () => { ecash: ['https://xec.paybutton.org'], bitcoincash: ['https://bch.paybutton.org'] } - + jest.spyOn(multiBlockchainClient, 'getUrls').mockReturnValue(mockUrls) - + const urls = multiBlockchainClient.getUrls() expect(urls).toHaveProperty('ecash') expect(urls).toHaveProperty('bitcoincash') @@ -1052,7 +1054,7 @@ describe('MultiBlockchainClient method coverage', () => { it('Should subscribe addresses', async () => { const validXecAddress = fromHash160('ecash', 'p2pkh', 'c5d2460186f7233c927e7db2dcc703c0e500b653') const validBchAddress = fromHash160('bitcoincash', 'p2pkh', 'd5e2470186f7233c927e7db2dcc703c0e500b653') - + const mockAddresses = [ { id: '1', @@ -1077,7 +1079,7 @@ describe('MultiBlockchainClient method coverage', () => { // Mock the client methods jest.spyOn(multiBlockchainClient, 'subscribeAddresses').mockImplementation(async (addresses) => { // Simulate subscription logic - return Promise.resolve() + return await Promise.resolve() }) await expect(multiBlockchainClient.subscribeAddresses(mockAddresses)).resolves.not.toThrow() @@ -1179,24 +1181,24 @@ describe('MultiBlockchainClient method coverage', () => { describe('Additional behavior and integration tests', () => { it('Should handle global multiBlockchainClient instance creation', () => { // Test that global instance is created and reused - const instance1 = require('../../services/chronikService').multiBlockchainClient - const instance2 = require('../../services/chronikService').multiBlockchainClient - + const instance1 = multiBlockchainClient + const instance2 = multiBlockchainClient + expect(instance1).toBe(instance2) // Should be same instance expect(instance1).toBeDefined() }) it('Should test fromHash160 with different address types', () => { const testHash160 = 'c5d2460186f7233c927e7db2dcc703c0e500b653' // 40 chars (20 bytes) - + // Test p2pkh const p2pkhAddress = fromHash160('ecash', 'p2pkh', testHash160) expect(p2pkhAddress).toMatch(/^ecash:q/) - + // Test p2sh const p2shAddress = fromHash160('ecash', 'p2sh', testHash160) expect(p2shAddress).toMatch(/^ecash:p/) - + // Test bitcoincash network const bchAddress = fromHash160('bitcoincash', 'p2pkh', testHash160) expect(bchAddress).toMatch(/^bitcoincash:q/) @@ -1204,21 +1206,20 @@ describe('Additional behavior and integration tests', () => { it('Should handle comprehensive outputScriptToAddress scenarios', () => { // Test various edge cases for outputScriptToAddress - + // Test with exact 40-character hash160 const validHash160 = 'c5d2460186f7233c927e7db2dcc703c0e500b653' // 40 chars const p2pkhScript = '76a914' + validHash160 + '88ac' const p2shScript = 'a914' + validHash160 + '87' - + expect(outputScriptToAddress('ecash', p2pkhScript)).toMatch(/^ecash:/) expect(outputScriptToAddress('ecash', p2shScript)).toMatch(/^ecash:/) - + // Test with exactly 39 characters (too short) - const shortHash160 = 'c5d2460186f7233c927e7db2dcc703c0e500b653'[0] // Missing one char const shortP2pkhScript = '76a914' + 'c5d2460186f7233c927e7db2dcc703c0e500b65' + '88ac' // 39 chars expect(outputScriptToAddress('ecash', shortP2pkhScript)).toBeUndefined() - - // Test with exactly 41 characters (too long) + + // Test with exactly 41 characters (too long) const longHash160 = validHash160 + 'a' // 41 chars const longP2pkhScript = '76a914' + longHash160 + '88ac' expect(outputScriptToAddress('ecash', longP2pkhScript)).toBeUndefined() @@ -1229,7 +1230,7 @@ describe('Additional behavior and integration tests', () => { const incompletePaymentIdScript = '6a' + '04' + '50415900' + '00' + '08' + '5051525354555657' + '03' + 'ab' const result = getNullDataScriptData(incompletePaymentIdScript) expect(result).toHaveProperty('paymentId', '') // Should ignore incomplete payment ID - + // Test with exactly the minimum required length const minimalScript = '6a' + '04' + '50415900' + '00' const minimalResult = getNullDataScriptData(minimalScript) @@ -1242,12 +1243,12 @@ describe('Additional behavior and integration tests', () => { expect(() => { getNullDataScriptData(exactMinScript) }).not.toThrow() - + // Test case sensitivity in protocol matching const uppercaseScript = '6A' + '04' + '50415900' + '00' + '08' + '5051525354555657' const result = getNullDataScriptData(uppercaseScript) expect(result).toHaveProperty('message') - + // Test mixed case const mixedCaseScript = '6a' + '04' + '50415900' + '00' + '08' + '5051525354555657' const mixedResult = getNullDataScriptData(mixedCaseScript) @@ -1258,14 +1259,14 @@ describe('Additional behavior and integration tests', () => { // Test with known valid addresses const validXecAddress = fromHash160('ecash', 'p2pkh', 'c5d2460186f7233c927e7db2dcc703c0e500b653') const result = toHash160(validXecAddress) - + expect(result.type).toBe('p2pkh') expect(result.hash160).toHaveLength(40) - + // Test with p2sh address const validP2shAddress = fromHash160('ecash', 'p2sh', 'c5d2460186f7233c927e7db2dcc703c0e500b653') const p2shResult = toHash160(validP2shAddress) - + expect(p2shResult.type).toBe('p2sh') expect(p2shResult.hash160).toHaveLength(40) }) @@ -1320,7 +1321,6 @@ describe('Regression: mempool + retries + onMessage + cache TTL', () => { expect(txMock).toHaveBeenCalledTimes(2) }) - it('clearOldMessages expires entries by TTL and from the correct maps', () => { process.env.WS_AUTH_KEY = 'test-auth-key' const client = new ChronikBlockchainClient('ecash') @@ -1329,7 +1329,7 @@ describe('Regression: mempool + retries + onMessage + cache TTL', () => { // build state: one old and one fresh in each map ;(client as any).lastProcessedMessages = { unconfirmed: { u_old: nowSec - 999999, u_new: nowSec - 1 }, - confirmed: { c_old: nowSec - 999999, c_new: nowSec - 1 } + confirmed: { c_old: nowSec - 999999, c_new: nowSec - 1 } } // run cleanup @@ -1349,7 +1349,6 @@ describe('WS onMessage matrix (no re-mocks)', () => { }) let client: any - let fetchAddressesArray: jest.Mock beforeEach(() => { jest.clearAllMocks() @@ -1368,7 +1367,6 @@ describe('WS onMessage matrix (no re-mocks)', () => { } as any // addressService mocks used by getAddressesForTransaction / waitForSyncing - ;({ fetchAddressesArray } = require('../../services/addressService')) fetchAddressesArray.mockResolvedValue([ { id: 'addr-1', @@ -1380,11 +1378,10 @@ describe('WS onMessage matrix (no re-mocks)', () => { ]) // never hit real payment layer in these tests - jest.spyOn(client as any, 'handleUpdateClientPaymentStatus').mockResolvedValue(undefined) + jest.spyOn(client, 'handleUpdateClientPaymentStatus').mockResolvedValue(undefined) }) it('handles TX_REMOVED_FROM_MEMPOOL → deletes unconfirmed txs', async () => { - const { fetchUnconfirmedTransactions, deleteTransactions } = require('../../services/transactionService') fetchUnconfirmedTransactions.mockResolvedValueOnce(['tx-to-del']) deleteTransactions.mockResolvedValueOnce(undefined) @@ -1402,11 +1399,11 @@ describe('WS onMessage matrix (no re-mocks)', () => { }) // deterministic related addresses - jest.spyOn(client as any, 'getRelatedAddressesForTransaction') + jest.spyOn(client, 'getRelatedAddressesForTransaction') .mockReturnValue(['ecash:qqkv9wr69ry2p9l53lxp635va4h86wv435995w8p2h']) // minimal transaction shape for downstream - jest.spyOn(client as any, 'getTransactionFromChronikTransaction') + jest.spyOn(client, 'getTransactionFromChronikTransaction') .mockResolvedValue({ hash: 'txCONF', amount: '0.01', @@ -1416,7 +1413,7 @@ describe('WS onMessage matrix (no re-mocks)', () => { opReturn: JSON.stringify({ message: { type: 'PAY', paymentId: 'pid-1' } }) }) - const paySpy = jest.spyOn(client as any, 'handleUpdateClientPaymentStatus') + const paySpy = jest.spyOn(client, 'handleUpdateClientPaymentStatus') await client.processWsMessage({ type: 'Tx', msgType: 'TX_CONFIRMED', txid: 'txCONF' }) @@ -1426,10 +1423,7 @@ describe('WS onMessage matrix (no re-mocks)', () => { }) it('handles TX_ADDED_TO_MEMPOOL → calls fetchTxWithRetry and upserts, triggers once', async () => { - const { upsertTransaction } = require('../../services/transactionService') - const { executeAddressTriggers } = require('../../services/triggerService') - - jest.spyOn(client as any, 'isAlreadyBeingProcessed').mockReturnValue(false) + jest.spyOn(client, 'isAlreadyBeingProcessed').mockReturnValue(false) jest.spyOn(client, 'fetchTxWithRetry').mockResolvedValue({ txid: 'txMEM', @@ -1437,7 +1431,7 @@ describe('WS onMessage matrix (no re-mocks)', () => { outputs: [{ sats: 10n, outputScript: '76a914c5d2460186f7233c927e7db2dcc703c0e500b65388ac' }] }) - jest.spyOn(client as any, 'getAddressesForTransaction').mockResolvedValue([ + jest.spyOn(client, 'getAddressesForTransaction').mockResolvedValue([ { address: { address: 'ecash:qqkv9wr69ry2p9l53lxp635va4h86wv435995w8p2h', networkId: 1 }, transaction: { @@ -1466,7 +1460,7 @@ describe('WS onMessage matrix (no re-mocks)', () => { it('TX_ADDED_TO_MEMPOOL → short-circuits when already being processed', async () => { const fetchSpy = jest.spyOn(client, 'fetchTxWithRetry') - jest.spyOn(client as any, 'isAlreadyBeingProcessed').mockReturnValue(true) + jest.spyOn(client, 'isAlreadyBeingProcessed').mockReturnValue(true) await client.processWsMessage({ type: 'Tx', msgType: 'TX_ADDED_TO_MEMPOOL', txid: 'dup' }) @@ -1478,14 +1472,16 @@ describe('WS onMessage matrix (no re-mocks)', () => { let attempts = 0 // drive underlying chronik.tx via the real fetchTxWithRetry - ;(client.chronik as any) = { tx: jest.fn(async () => { - attempts += 1 - if (attempts < 3) throw new Error('Transaction not found in the index') - return { txid: 'tx404', inputs: [], outputs: [] } - })} + ;(client.chronik) = { + tx: jest.fn(async () => { + attempts += 1 + if (attempts < 3) throw new Error('Transaction not found in the index') + return { txid: 'tx404', inputs: [], outputs: [] } + }) + } - jest.spyOn(client as any, 'isAlreadyBeingProcessed').mockReturnValue(false) - jest.spyOn(client as any, 'getAddressesForTransaction').mockResolvedValue([]) + jest.spyOn(client, 'isAlreadyBeingProcessed').mockReturnValue(false) + jest.spyOn(client, 'getAddressesForTransaction').mockResolvedValue([]) const p = client.processWsMessage({ type: 'Tx', msgType: 'TX_ADDED_TO_MEMPOOL', txid: 'tx404' }) @@ -1503,7 +1499,7 @@ describe('WS onMessage matrix (no re-mocks)', () => { it('handles Block → BLK_FINALIZED triggers sync and clears cache', async () => { client.initializing = false client.confirmedTxsHashesFromLastBlock = ['A', 'B'] - const syncSpy = jest.spyOn(client as any, 'syncBlockTransactions').mockResolvedValue(undefined) + const syncSpy = jest.spyOn(client, 'syncBlockTransactions').mockResolvedValue(undefined) await client.processWsMessage({ type: 'Block', msgType: 'BLK_FINALIZED', blockHash: 'bh', blockHeight: 123 }) diff --git a/tests/unittests/handleUpdateClientPaymentStatus.test.ts b/tests/unittests/handleUpdateClientPaymentStatus.test.ts index 5366876d..dd07dc69 100644 --- a/tests/unittests/handleUpdateClientPaymentStatus.test.ts +++ b/tests/unittests/handleUpdateClientPaymentStatus.test.ts @@ -11,7 +11,7 @@ jest.mock('../../services/clientPaymentService', () => ({ })) // Mock other heavy dependencies -jest.mock('chronik-client-cashtokens', () => ({ +jest.mock('chronik-client', () => ({ ChronikClient: { useStrategy: jest.fn().mockResolvedValue({ ws: jest.fn().mockReturnValue({ From 42c015080c91ad2fe9e65f1bca4988f7bd5de129 Mon Sep 17 00:00:00 2001 From: David Klakurka Date: Sun, 21 Dec 2025 14:57:41 -0800 Subject: [PATCH 2/2] yarn lock --- yarn.lock | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 76913219..bd38b666 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2287,15 +2287,16 @@ chokidar@^4.0.3: dependencies: readdirp "^4.0.1" -chronik-client-cashtokens@^3.1.1-rc0: - version "3.1.1-rc0" - resolved "https://registry.yarnpkg.com/chronik-client-cashtokens/-/chronik-client-cashtokens-3.1.1-rc0.tgz#e5e4a3538e8010b70623974a731bf2712506c5e3" - integrity sha512-jnFnog+hVd9u8bik+sf2SRVwr4ETN8Gnq6HpFRdPkM5UtMu7boCjQVrKHOXKb9AyaCFKUnreK9mBEL/l9dAjMg== +chronik-client@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chronik-client/-/chronik-client-4.1.0.tgz#f49a07565064335f1d576230bf77546a9bc29e8b" + integrity sha512-RA7gXMTmWW3ek29ACvHGfcJxGUBMxtcvMCCV7EJLywYCCYyCDmjdcTVIHEqEFF0QHj6F5PJi5nvA65cAtBnr5Q== dependencies: "@types/ws" "^8.2.1" axios "^1.6.3" - ecashaddrjs "file:../.cache/yarn/v6/npm-chronik-client-cashtokens-3.1.1-rc0-e5e4a3538e8010b70623974a731bf2712506c5e3-integrity/node_modules/ecashaddrjs" + ecashaddrjs "^2.0.0" isomorphic-ws "^4.0.1" + long "^4.0.0" protobufjs "^6.8.8" ws "^8.3.0" @@ -2830,7 +2831,7 @@ ecashaddrjs@^1.0.7: big-integer "1.6.36" bs58check "^3.0.1" -ecashaddrjs@^2.0.0, "ecashaddrjs@file:../../../.cache/yarn/v6/npm-chronik-client-cashtokens-3.1.1-rc0-e5e4a3538e8010b70623974a731bf2712506c5e3-integrity/node_modules/ecashaddrjs": +ecashaddrjs@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ecashaddrjs/-/ecashaddrjs-2.0.0.tgz#d45ede7fb6168815dbcf664b8e0a6872e485d874" integrity sha512-EvK1V4D3+nIEoD0ggy/b0F4lW39/72R9aOs/scm6kxMVuXu16btc+H74eQv7okNfXaQWKgolEekZkQ6wfcMMLw==