From 8ee83528ab31be484c3f26790ee53fe58e1470cb Mon Sep 17 00:00:00 2001 From: Andre Carneiro Date: Fri, 4 Jul 2025 15:23:51 -0300 Subject: [PATCH 01/10] feat: single address policy --- src/types.ts | 16 ++++++++++++++-- src/utils/storage.ts | 9 +++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/types.ts b/src/types.ts index d70f0800a..63b8ed284 100644 --- a/src/types.ts +++ b/src/types.ts @@ -327,6 +327,7 @@ export interface IWalletAccessData { export enum SCANNING_POLICY { GAP_LIMIT = 'gap-limit', INDEX_LIMIT = 'index-limit', + SINGLE = 'single', } export interface IGapLimitAddressScanPolicy { @@ -340,6 +341,11 @@ export interface IIndexLimitAddressScanPolicy { endIndex: number; } +export interface ISingleAddressScanPolicy { + policy: SCANNING_POLICY.SINGLE; + index?: number; +} + /** * This is a request from the scanning policy to load `count` addresses starting from nextIndex. */ @@ -348,9 +354,9 @@ export interface IScanPolicyLoadAddresses { count: number; } -export type AddressScanPolicy = SCANNING_POLICY.GAP_LIMIT | SCANNING_POLICY.INDEX_LIMIT; +export type AddressScanPolicy = SCANNING_POLICY.GAP_LIMIT | SCANNING_POLICY.INDEX_LIMIT | SCANNING_POLICY.SINGLE; -export type AddressScanPolicyData = IGapLimitAddressScanPolicy | IIndexLimitAddressScanPolicy; +export type AddressScanPolicyData = IGapLimitAddressScanPolicy | IIndexLimitAddressScanPolicy | ISingleAddressScanPolicy; export function isGapLimitScanPolicy( scanPolicyData: AddressScanPolicyData @@ -364,6 +370,12 @@ export function isIndexLimitScanPolicy( return scanPolicyData.policy === SCANNING_POLICY.INDEX_LIMIT; } +export function isSingleScanPolicy( + scanPolicyData: AddressScanPolicyData +): scanPolicyData is ISingleAddressScanPolicy { + return scanPolicyData.policy === SCANNING_POLICY.SINGLE; +} + export interface IWalletData { lastLoadedAddressIndex: number; lastUsedAddressIndex: number; diff --git a/src/utils/storage.ts b/src/utils/storage.ts index 96f90dce6..d45b0a769 100644 --- a/src/utils/storage.ts +++ b/src/utils/storage.ts @@ -268,6 +268,12 @@ export async function scanPolicyStartAddresses( nextIndex: limits.startIndex, count: limits.endIndex - limits.startIndex + 1, }; + case SCANNING_POLICY.SINGLE: + // Single scanning always loads 1 address only + return { + nextIndex: 0, + count: 1, + }; case SCANNING_POLICY.GAP_LIMIT: default: return { @@ -292,6 +298,9 @@ export async function checkScanningPolicy( return checkIndexLimit(storage); case SCANNING_POLICY.GAP_LIMIT: return checkGapLimit(storage); + case SCANNING_POLICY.SINGLE: + // Single scanning policy never needs to load another address. + return null; default: return null; } From e4b56e3382891806748fbb3655d6907101f6c75b Mon Sep 17 00:00:00 2001 From: Andre Carneiro Date: Wed, 16 Jul 2025 13:46:37 -0300 Subject: [PATCH 02/10] chore: linter changes --- src/types.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/types.ts b/src/types.ts index 63b8ed284..5ff273c6d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -354,9 +354,15 @@ export interface IScanPolicyLoadAddresses { count: number; } -export type AddressScanPolicy = SCANNING_POLICY.GAP_LIMIT | SCANNING_POLICY.INDEX_LIMIT | SCANNING_POLICY.SINGLE; - -export type AddressScanPolicyData = IGapLimitAddressScanPolicy | IIndexLimitAddressScanPolicy | ISingleAddressScanPolicy; +export type AddressScanPolicy = + | SCANNING_POLICY.GAP_LIMIT + | SCANNING_POLICY.INDEX_LIMIT + | SCANNING_POLICY.SINGLE; + +export type AddressScanPolicyData = + | IGapLimitAddressScanPolicy + | IIndexLimitAddressScanPolicy + | ISingleAddressScanPolicy; export function isGapLimitScanPolicy( scanPolicyData: AddressScanPolicyData From fadf66bd3205dd0f406db58379b664ece827877e Mon Sep 17 00:00:00 2001 From: Andre Carneiro Date: Wed, 16 Jul 2025 23:17:24 -0300 Subject: [PATCH 03/10] feat: load single address at index --- src/new/wallet.js | 5 ++++- src/utils/storage.ts | 9 +++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/new/wallet.js b/src/new/wallet.js index 703c1810e..54ffd2747 100644 --- a/src/new/wallet.js +++ b/src/new/wallet.js @@ -615,7 +615,10 @@ class HathorWallet extends EventEmitter { } else { address = await deriveAddressP2SH(index, this.storage); } - await this.storage.saveAddress(address); + const policyData = await this.storage.getScanningPolicyData(); + if (policyData.policy !== SCANNING_POLICY.SINGLE) { + await this.storage.saveAddress(address); + } } return address.base58; } diff --git a/src/utils/storage.ts b/src/utils/storage.ts index d45b0a769..99b526d3f 100644 --- a/src/utils/storage.ts +++ b/src/utils/storage.ts @@ -24,6 +24,9 @@ import { WalletType, IUtxo, ITokenData, + ISingleAddressScanPolicy, + IIndexLimitAddressScanPolicy, + AddressScanPolicyData, } from '../types'; import walletApi from '../api/wallet'; import helpers from './helpers'; @@ -256,7 +259,8 @@ export async function scanPolicyStartAddresses( storage: IStorage ): Promise { const scanPolicy = await storage.getScanningPolicy(); - let limits; + let limits: Omit | null; + let policyData: AddressScanPolicyData; switch (scanPolicy) { case SCANNING_POLICY.INDEX_LIMIT: limits = await storage.getIndexLimit(); @@ -270,8 +274,9 @@ export async function scanPolicyStartAddresses( }; case SCANNING_POLICY.SINGLE: // Single scanning always loads 1 address only + policyData = (await storage.getScanningPolicyData()) as ISingleAddressScanPolicy; return { - nextIndex: 0, + nextIndex: policyData.index ?? 0, count: 1, }; case SCANNING_POLICY.GAP_LIMIT: From 1b19f8bf89dea161e7aaadce50f0a77430705017 Mon Sep 17 00:00:00 2001 From: Andre Carneiro Date: Thu, 17 Jul 2025 12:41:54 -0300 Subject: [PATCH 04/10] tests(integration): single address policy tests --- .../integration/hathorwallet_others.test.ts | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/__tests__/integration/hathorwallet_others.test.ts b/__tests__/integration/hathorwallet_others.test.ts index 67a32c2bf..50316d40f 100644 --- a/__tests__/integration/hathorwallet_others.test.ts +++ b/__tests__/integration/hathorwallet_others.test.ts @@ -1739,3 +1739,70 @@ describe('index-limit address scanning policy', () => { await expect(hWallet.storage.store.addressCount()).resolves.toEqual(25); }); }); + +describe('single address scanning policy', () => { + /** @type HathorWallet */ + let hWallet; + beforeAll(async () => { + const walletData = precalculationHelpers.test.getPrecalculatedWallet(); + hWallet = await generateWalletHelper({ + seed: walletData.words, + addresses: walletData.addresses, + scanPolicy: { + policy: 'single', + index: 5, + }, + }); + }); + + afterAll(async () => { + await hWallet.stop(); + }); + + it('should start a wallet configured to single address', async () => { + await expect(hWallet.storage.store.addressCount()).resolves.toEqual(1); + + // Send tokens to address 5 (the loaded one) + const address5 = hWallet.getAddressAtIndex(5); + await GenesisWalletHelper.injectFunds(hWallet, address5, 10n); + await expect(hWallet.storage.store.addressCount()).resolves.toEqual(1); + + // Send more transactions from the same wallet to the same address + const tx1 = await hWallet.sendTransaction(address5, 1n); + await waitForTxReceived(hWallet, tx1.hash); + await expect(hWallet.storage.store.addressCount()).resolves.toEqual(1); + await expect(hWallet.getBalance()).resolves.toEqual( + expect.arrayContaining([ + expect.objectContaining({ + balance: expect.objectContaining({ unlocked: 10n }), + }), + ]) + ); + + // Send a tx to an unloaded address before the current one + const address0 = hWallet.getAddressAtIndex(0); + const tx2 = await hWallet.sendTransaction(address0, 1n); + await waitForTxReceived(hWallet, tx2.hash); + await expect(hWallet.storage.store.addressCount()).resolves.toEqual(1); + await expect(hWallet.getBalance()).resolves.toEqual( + expect.arrayContaining([ + expect.objectContaining({ + balance: expect.objectContaining({ unlocked: 9n }), + }), + ]) + ); + + // Send a tx to an unloaded address before the current one + const address10 = hWallet.getAddressAtIndex(10); + const tx3 = await hWallet.sendTransaction(address10, 1n); + await waitForTxReceived(hWallet, tx3.hash); + await expect(hWallet.storage.store.addressCount()).resolves.toEqual(1); + await expect(hWallet.getBalance()).resolves.toEqual( + expect.arrayContaining([ + expect.objectContaining({ + balance: expect.objectContaining({ unlocked: 8n }), + }), + ]) + ); + }); +}); From 46d3c9a0ece863f0bb473c4b5149f15363350b41 Mon Sep 17 00:00:00 2001 From: Andre Carneiro Date: Thu, 17 Jul 2025 13:24:52 -0300 Subject: [PATCH 05/10] tests(integration): await promises --- __tests__/integration/hathorwallet_others.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/__tests__/integration/hathorwallet_others.test.ts b/__tests__/integration/hathorwallet_others.test.ts index 50316d40f..cae950743 100644 --- a/__tests__/integration/hathorwallet_others.test.ts +++ b/__tests__/integration/hathorwallet_others.test.ts @@ -1763,7 +1763,7 @@ describe('single address scanning policy', () => { await expect(hWallet.storage.store.addressCount()).resolves.toEqual(1); // Send tokens to address 5 (the loaded one) - const address5 = hWallet.getAddressAtIndex(5); + const address5 = await hWallet.getAddressAtIndex(5); await GenesisWalletHelper.injectFunds(hWallet, address5, 10n); await expect(hWallet.storage.store.addressCount()).resolves.toEqual(1); @@ -1780,7 +1780,7 @@ describe('single address scanning policy', () => { ); // Send a tx to an unloaded address before the current one - const address0 = hWallet.getAddressAtIndex(0); + const address0 = await hWallet.getAddressAtIndex(0); const tx2 = await hWallet.sendTransaction(address0, 1n); await waitForTxReceived(hWallet, tx2.hash); await expect(hWallet.storage.store.addressCount()).resolves.toEqual(1); @@ -1793,7 +1793,7 @@ describe('single address scanning policy', () => { ); // Send a tx to an unloaded address before the current one - const address10 = hWallet.getAddressAtIndex(10); + const address10 = await hWallet.getAddressAtIndex(10); const tx3 = await hWallet.sendTransaction(address10, 1n); await waitForTxReceived(hWallet, tx3.hash); await expect(hWallet.storage.store.addressCount()).resolves.toEqual(1); From 4167c6ca39a83b70c55f282af86fe7a439651266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Fri, 12 Dec 2025 12:06:34 -0300 Subject: [PATCH 06/10] chore: wallet-service address policy --- src/new/wallet.ts | 11 +++++++++++ src/wallet/wallet.ts | 22 +++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/new/wallet.ts b/src/new/wallet.ts index a056c8f01..2cfdd03a6 100644 --- a/src/new/wallet.ts +++ b/src/new/wallet.ts @@ -1594,6 +1594,17 @@ class HathorWallet extends EventEmitter { this.conn.on('wallet-update', this.handleWebsocketMsg); if (this.preCalculatedAddresses) { + // Single address policy should not load extraneous addresses. + if (this.scanPolicy?.policy === SCANNING_POLICY.SINGLE) { + const index = this.scanPolicy?.index ?? 0; + if (index < this.preCalculatedAddresses.length) { + await this.storage.saveAddress({ + base58: this.preCalculatedAddresses[index], + bip32AddressIndex: index, + }); + } + } + for (const [index, addr] of this.preCalculatedAddresses.entries()) { await this.storage.saveAddress({ base58: addr, diff --git a/src/wallet/wallet.ts b/src/wallet/wallet.ts index 07bcf58ef..ffdd536cd 100644 --- a/src/wallet/wallet.ts +++ b/src/wallet/wallet.ts @@ -97,6 +97,9 @@ import { WalletType, ITokenData, TokenVersion, + AddressScanPolicy, + SCANNING_POLICY, + AddressScanPolicyData, } from '../types'; import { Fee } from '../utils/fee'; @@ -163,6 +166,8 @@ class HathorWalletServiceWallet extends EventEmitter implements IHathorWallet { // Flag to indicate if the websocket connection is enabled private readonly _isWsEnabled: boolean; + private scanPolicy: AddressScanPolicyData | null; + public storage: IStorage; constructor({ @@ -175,6 +180,7 @@ class HathorWalletServiceWallet extends EventEmitter implements IHathorWallet { passphrase = '', enableWs = true, storage = null, + scanPolicy = null, }: { requestPassword: () => Promise; seed?: string | null; @@ -185,6 +191,7 @@ class HathorWalletServiceWallet extends EventEmitter implements IHathorWallet { passphrase?: string; enableWs?: boolean; storage?: IStorage | null; + scanPolicy?: AddressScanPolicyData | null; }) { super(); @@ -246,7 +253,7 @@ class HathorWalletServiceWallet extends EventEmitter implements IHathorWallet { this.newAddresses = []; this.indexToUse = -1; - // TODO should we have a debug mode? + this.scanPolicy = scanPolicy; } /** @@ -878,6 +885,19 @@ class HathorWalletServiceWallet extends EventEmitter implements IHathorWallet { // asynchronous, so we will get an empty or partial array of addresses if they are not all loaded. this.failIfWalletNotReady(); } + + if (this.scanPolicy?.policy === SCANNING_POLICY.SINGLE) { + const addressIndex = this.scanPolicy.index ?? 0; + const data = await walletApi.getAddresses(this, addressIndex); + this.newAddresses = data.addresses.map(addr => ({ + address: addr.address, + index: addr.index, + addressPath: `m/44'/280'/0'/0/${addr.index}`, + })); + this.indexToUse = 0; + return; + } + const data = await walletApi.getNewAddresses(this); this.newAddresses = data.addresses; this.indexToUse = 0; From b1c66ad94b42f7752f71a9a7b0d7a9d897eed999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Fri, 12 Dec 2025 13:13:30 -0300 Subject: [PATCH 07/10] chore: move testes around --- .../integration/hathorwallet_others.test.ts | 176 +++++++++--------- 1 file changed, 88 insertions(+), 88 deletions(-) diff --git a/__tests__/integration/hathorwallet_others.test.ts b/__tests__/integration/hathorwallet_others.test.ts index 0f36c4243..53ec10771 100644 --- a/__tests__/integration/hathorwallet_others.test.ts +++ b/__tests__/integration/hathorwallet_others.test.ts @@ -1614,94 +1614,6 @@ describe('getAuthorityUtxos', () => { }); }); -// This section tests methods that have side effects impacting the whole wallet. Executing it last. -describe('internal methods', () => { - /** @type HathorWallet */ - let gWallet; - /** @type HathorWallet */ - let hWallet; - beforeAll(async () => { - const { hWallet: ghWallet } = await GenesisWalletHelper.getSingleton(); - gWallet = ghWallet; - hWallet = await generateWalletHelper(); - }); - - afterAll(async () => { - hWallet.stop(); - await GenesisWalletHelper.clearListeners(); - await gWallet.stop(); - }); - - it('should test the debug methods', async () => { - expect(gWallet.debug).toStrictEqual(false); - - gWallet.enableDebugMode(); - expect(gWallet.debug).toStrictEqual(true); - - gWallet.disableDebugMode(); - expect(gWallet.debug).toStrictEqual(false); - }); - - it('should test network-related methods', async () => { - // GetServerUrl fetching from the live fullnode connection - expect(await gWallet.getServerUrl()).toStrictEqual(FULLNODE_URL); - expect(await gWallet.getNetwork()).toStrictEqual(NETWORK_NAME); - expect(await gWallet.getNetworkObject()).toMatchObject({ - name: NETWORK_NAME, - versionBytes: { p2pkh: 73, p2sh: 135 }, // Calculated for the privnet.py config file - bitcoreNetwork: { - name: expect.stringContaining(NETWORK_NAME), - alias: 'test', // this is the alias for the testnet network - pubkeyhash: 73, - scripthash: 135, - }, - }); - - // GetVersionData fetching from the live fullnode server - expect(await gWallet.getVersionData()).toMatchObject({ - timestamp: expect.any(Number), - version: expect.any(String), - network: FULLNODE_NETWORK_NAME, - minWeight: expect.any(Number), - minTxWeight: expect.any(Number), - minTxWeightCoefficient: expect.any(Number), - minTxWeightK: expect.any(Number), - tokenDepositPercentage: 0.01, - rewardSpendMinBlocks: expect.any(Number), - maxNumberInputs: 255, - maxNumberOutputs: 255, - }); - }); - - it('should change servers', async () => { - // Changing from our integration test privatenet to the testnet - gWallet.changeServer('https://node1.testnet.hathor.network/v1a/'); - const serverChangeTime = Date.now().valueOf(); - await delay(100); - - // Validating the server change with getVersionData - let networkData = await gWallet.getVersionData(); - expect(networkData.timestamp).toBeGreaterThan(serverChangeTime); - expect(networkData.network).toMatch(/^testnet.*/); - - await gWallet.changeServer(FULLNODE_URL); - await delay(100); - - // Reverting to the privatenet - networkData = await gWallet.getVersionData(); - expect(networkData.timestamp).toBeGreaterThan(serverChangeTime + 200); - expect(networkData.network).toStrictEqual(FULLNODE_NETWORK_NAME); - }); - - it('should reload the storage', async () => { - await GenesisWalletHelper.injectFunds(hWallet, await hWallet.getAddressAtIndex(0), 10n); - const spy = jest.spyOn(hWallet.storage, 'processHistory'); - // Simulate that we received an event of the connection becoming active - await hWallet.onConnectionChangedState(ConnectionState.CONNECTED); - expect(spy).toHaveBeenCalledTimes(1); - }); -}); - describe('index-limit address scanning policy', () => { /** @type HathorWallet */ let hWallet; @@ -1806,3 +1718,91 @@ describe('single address scanning policy', () => { ); }); }); + +// This section tests methods that have side effects impacting the whole wallet. Executing it last. +describe('internal methods', () => { + /** @type HathorWallet */ + let gWallet; + /** @type HathorWallet */ + let hWallet; + beforeAll(async () => { + const { hWallet: ghWallet } = await GenesisWalletHelper.getSingleton(); + gWallet = ghWallet; + hWallet = await generateWalletHelper(); + }); + + afterAll(async () => { + hWallet.stop(); + await GenesisWalletHelper.clearListeners(); + await gWallet.stop(); + }); + + it('should test the debug methods', async () => { + expect(gWallet.debug).toStrictEqual(false); + + gWallet.enableDebugMode(); + expect(gWallet.debug).toStrictEqual(true); + + gWallet.disableDebugMode(); + expect(gWallet.debug).toStrictEqual(false); + }); + + it('should test network-related methods', async () => { + // GetServerUrl fetching from the live fullnode connection + expect(await gWallet.getServerUrl()).toStrictEqual(FULLNODE_URL); + expect(await gWallet.getNetwork()).toStrictEqual(NETWORK_NAME); + expect(await gWallet.getNetworkObject()).toMatchObject({ + name: NETWORK_NAME, + versionBytes: { p2pkh: 73, p2sh: 135 }, // Calculated for the privnet.py config file + bitcoreNetwork: { + name: expect.stringContaining(NETWORK_NAME), + alias: 'test', // this is the alias for the testnet network + pubkeyhash: 73, + scripthash: 135, + }, + }); + + // GetVersionData fetching from the live fullnode server + expect(await gWallet.getVersionData()).toMatchObject({ + timestamp: expect.any(Number), + version: expect.any(String), + network: FULLNODE_NETWORK_NAME, + minWeight: expect.any(Number), + minTxWeight: expect.any(Number), + minTxWeightCoefficient: expect.any(Number), + minTxWeightK: expect.any(Number), + tokenDepositPercentage: 0.01, + rewardSpendMinBlocks: expect.any(Number), + maxNumberInputs: 255, + maxNumberOutputs: 255, + }); + }); + + it('should change servers', async () => { + // Changing from our integration test privatenet to the testnet + gWallet.changeServer('https://node1.testnet.hathor.network/v1a/'); + const serverChangeTime = Date.now().valueOf(); + await delay(100); + + // Validating the server change with getVersionData + let networkData = await gWallet.getVersionData(); + expect(networkData.timestamp).toBeGreaterThan(serverChangeTime); + expect(networkData.network).toMatch(/^testnet.*/); + + await gWallet.changeServer(FULLNODE_URL); + await delay(100); + + // Reverting to the privatenet + networkData = await gWallet.getVersionData(); + expect(networkData.timestamp).toBeGreaterThan(serverChangeTime + 200); + expect(networkData.network).toStrictEqual(FULLNODE_NETWORK_NAME); + }); + + it('should reload the storage', async () => { + await GenesisWalletHelper.injectFunds(hWallet, await hWallet.getAddressAtIndex(0), 10n); + const spy = jest.spyOn(hWallet.storage, 'processHistory'); + // Simulate that we received an event of the connection becoming active + await hWallet.onConnectionChangedState(ConnectionState.CONNECTED); + expect(spy).toHaveBeenCalledTimes(1); + }); +}); From 5963c9815633a5628f643cf4cfa0027a4f71010b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Fri, 12 Dec 2025 13:14:50 -0300 Subject: [PATCH 08/10] chore: linter changes --- src/wallet/wallet.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wallet/wallet.ts b/src/wallet/wallet.ts index ffdd536cd..52b44a440 100644 --- a/src/wallet/wallet.ts +++ b/src/wallet/wallet.ts @@ -97,7 +97,6 @@ import { WalletType, ITokenData, TokenVersion, - AddressScanPolicy, SCANNING_POLICY, AddressScanPolicyData, } from '../types'; From 1e0218acc7bd17328cd69cb165446a61a326f158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Fri, 12 Dec 2025 13:34:38 -0300 Subject: [PATCH 09/10] chore: add balance args --- __tests__/integration/hathorwallet_others.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/__tests__/integration/hathorwallet_others.test.ts b/__tests__/integration/hathorwallet_others.test.ts index 53ec10771..4ad2276c5 100644 --- a/__tests__/integration/hathorwallet_others.test.ts +++ b/__tests__/integration/hathorwallet_others.test.ts @@ -1683,7 +1683,7 @@ describe('single address scanning policy', () => { const tx1 = await hWallet.sendTransaction(address5, 1n); await waitForTxReceived(hWallet, tx1.hash); await expect(hWallet.storage.store.addressCount()).resolves.toEqual(1); - await expect(hWallet.getBalance()).resolves.toEqual( + await expect(hWallet.getBalance('00')).resolves.toEqual( expect.arrayContaining([ expect.objectContaining({ balance: expect.objectContaining({ unlocked: 10n }), @@ -1696,7 +1696,7 @@ describe('single address scanning policy', () => { const tx2 = await hWallet.sendTransaction(address0, 1n); await waitForTxReceived(hWallet, tx2.hash); await expect(hWallet.storage.store.addressCount()).resolves.toEqual(1); - await expect(hWallet.getBalance()).resolves.toEqual( + await expect(hWallet.getBalance('00')).resolves.toEqual( expect.arrayContaining([ expect.objectContaining({ balance: expect.objectContaining({ unlocked: 9n }), @@ -1709,7 +1709,7 @@ describe('single address scanning policy', () => { const tx3 = await hWallet.sendTransaction(address10, 1n); await waitForTxReceived(hWallet, tx3.hash); await expect(hWallet.storage.store.addressCount()).resolves.toEqual(1); - await expect(hWallet.getBalance()).resolves.toEqual( + await expect(hWallet.getBalance('00')).resolves.toEqual( expect.arrayContaining([ expect.objectContaining({ balance: expect.objectContaining({ unlocked: 8n }), From 4dd2370b60120269136ec4c2c04708cbed9b6bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Fri, 12 Dec 2025 14:15:21 -0300 Subject: [PATCH 10/10] chore: use isSingleAddressPolicy --- src/new/wallet.ts | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/new/wallet.ts b/src/new/wallet.ts index 2cfdd03a6..86e67b207 100644 --- a/src/new/wallet.ts +++ b/src/new/wallet.ts @@ -59,6 +59,7 @@ import { WalletType, HistorySyncMode, getDefaultLogger, + isSingleScanPolicy, } from '../types'; import { TokenVersion } from '../models/enum'; import transactionUtils from '../utils/transaction'; @@ -660,7 +661,7 @@ class HathorWallet extends EventEmitter { address = await deriveAddressP2SH(index, this.storage); } const policyData = await this.storage.getScanningPolicyData(); - if (policyData.policy !== SCANNING_POLICY.SINGLE) { + if (!isSingleScanPolicy(policyData)) { await this.storage.saveAddress(address); } } @@ -1594,17 +1595,6 @@ class HathorWallet extends EventEmitter { this.conn.on('wallet-update', this.handleWebsocketMsg); if (this.preCalculatedAddresses) { - // Single address policy should not load extraneous addresses. - if (this.scanPolicy?.policy === SCANNING_POLICY.SINGLE) { - const index = this.scanPolicy?.index ?? 0; - if (index < this.preCalculatedAddresses.length) { - await this.storage.saveAddress({ - base58: this.preCalculatedAddresses[index], - bip32AddressIndex: index, - }); - } - } - for (const [index, addr] of this.preCalculatedAddresses.entries()) { await this.storage.saveAddress({ base58: addr,