diff --git a/common/changes/@cityofzion/blockchain-service/CU-86a6xp22g_2025-02-24-20-20.json b/common/changes/@cityofzion/blockchain-service/CU-86a6xp22g_2025-02-24-20-20.json new file mode 100644 index 0000000..d5a7e8d --- /dev/null +++ b/common/changes/@cityofzion/blockchain-service/CU-86a6xp22g_2025-02-24-20-20.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@cityofzion/blockchain-service", + "comment": "Add functionality to generate address until a specific index", + "type": "minor" + } + ], + "packageName": "@cityofzion/blockchain-service" +} \ No newline at end of file diff --git a/common/changes/@cityofzion/bs-ethereum/CU-86a6xp22g_2025-02-24-20-20.json b/common/changes/@cityofzion/bs-ethereum/CU-86a6xp22g_2025-02-24-20-20.json new file mode 100644 index 0000000..bd36542 --- /dev/null +++ b/common/changes/@cityofzion/bs-ethereum/CU-86a6xp22g_2025-02-24-20-20.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@cityofzion/bs-ethereum", + "comment": "Add functionality to generate address until a specific index", + "type": "minor" + } + ], + "packageName": "@cityofzion/bs-ethereum" +} \ No newline at end of file diff --git a/common/changes/@cityofzion/bs-neo-legacy/CU-86a6xp22g_2025-02-24-20-20.json b/common/changes/@cityofzion/bs-neo-legacy/CU-86a6xp22g_2025-02-24-20-20.json new file mode 100644 index 0000000..794db23 --- /dev/null +++ b/common/changes/@cityofzion/bs-neo-legacy/CU-86a6xp22g_2025-02-24-20-20.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@cityofzion/bs-neo-legacy", + "comment": "Add functionality to generate address until a specific index", + "type": "minor" + } + ], + "packageName": "@cityofzion/bs-neo-legacy" +} \ No newline at end of file diff --git a/common/changes/@cityofzion/bs-neo3/CU-86a6xp22g_2025-02-24-20-20.json b/common/changes/@cityofzion/bs-neo3/CU-86a6xp22g_2025-02-24-20-20.json new file mode 100644 index 0000000..3858318 --- /dev/null +++ b/common/changes/@cityofzion/bs-neo3/CU-86a6xp22g_2025-02-24-20-20.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@cityofzion/bs-neo3", + "comment": "Add functionality to generate address until a specific index", + "type": "minor" + } + ], + "packageName": "@cityofzion/bs-neo3" +} \ No newline at end of file diff --git a/packages/blockchain-service/src/BSAggregator.ts b/packages/blockchain-service/src/BSAggregator.ts index 8ef59ba..2f26921 100644 --- a/packages/blockchain-service/src/BSAggregator.ts +++ b/packages/blockchain-service/src/BSAggregator.ts @@ -1,4 +1,4 @@ -import { fetchAccountsForBlockchainServices } from './functions' +import { fetchAccountsForBlockchainServices, generateAccountUntilIndexForBlockchainService } from './functions' import { Account, BlockchainService } from './interfaces' export class BSAggregator { @@ -51,9 +51,19 @@ export class BSAggregator { return this.#blockchainServices.filter(bs => bs.validateEncrypted(keyOrJson)).map(bs => bs.name) } - async generateAccountsFromMnemonic(mnemonic: string): Promise[]>> { - return fetchAccountsForBlockchainServices( + async generateAccountsFromMnemonic(mnemonic: string, untilIndex?: number): Promise[]>> { + if (untilIndex === undefined) { + return fetchAccountsForBlockchainServices( + this.#blockchainServices, + async (service: BlockchainService, index: number) => { + return service.generateAccountFromMnemonic(mnemonic, index) + } + ) + } + + return generateAccountUntilIndexForBlockchainService( this.#blockchainServices, + untilIndex, async (service: BlockchainService, index: number) => { return service.generateAccountFromMnemonic(mnemonic, index) } diff --git a/packages/blockchain-service/src/functions.ts b/packages/blockchain-service/src/functions.ts index 57a2fb2..781ef4b 100644 --- a/packages/blockchain-service/src/functions.ts +++ b/packages/blockchain-service/src/functions.ts @@ -134,7 +134,36 @@ export async function fetchAccountsForBlockchainServices( + blockchainServices: BlockchainService[], + untilIndex: number, + getAccountCallback: (service: BlockchainService, index: number) => Promise> +): Promise[]>> { + if (untilIndex < 0) { + throw new Error('Invalid index') + } + + const accountsByBlockchainService = new Map[]>() + + const promises = blockchainServices.map(async service => { + let index = 0 + const accounts: Account[] = [] + + while (index <= untilIndex) { + const generatedAccount = await getAccountCallback(service, index) + accounts.push(generatedAccount) + index++ + } + + accountsByBlockchainService.set(service.name, accounts) + }) + + await Promise.allSettled(promises) return accountsByBlockchainService } diff --git a/packages/blockchain-service/src/interfaces.ts b/packages/blockchain-service/src/interfaces.ts index c4f571d..58c7867 100644 --- a/packages/blockchain-service/src/interfaces.ts +++ b/packages/blockchain-service/src/interfaces.ts @@ -246,7 +246,7 @@ export type GetLedgerTransport = (account: Accou export interface LedgerService { emitter: LedgerServiceEmitter getLedgerTransport?: GetLedgerTransport - getAccounts(transport: Transport): Promise[]> + getAccounts(transport: Transport, untilIndex?: number): Promise[]> getAccount(transport: Transport, index: number): Promise> } diff --git a/packages/bs-ethereum/src/__tests__/EthersLedgerServiceEthereum.spec.ts b/packages/bs-ethereum/src/__tests__/EthersLedgerServiceEthereum.spec.ts index 845b849..d4c153f 100644 --- a/packages/bs-ethereum/src/__tests__/EthersLedgerServiceEthereum.spec.ts +++ b/packages/bs-ethereum/src/__tests__/EthersLedgerServiceEthereum.spec.ts @@ -119,7 +119,7 @@ describe.skip('EthersLedgerServiceEthereum', () => { expect(signatureAddress).toEqual(address) }, 60000) - it('Should be able to get all accounts', async () => { + it('Should be able to get all accounts automatically', async () => { const accounts = await ledgerService.getAccounts(transport) expect(accounts.length).toBeGreaterThan(1) @@ -135,6 +135,22 @@ describe.skip('EthersLedgerServiceEthereum', () => { }) }, 60000) + it('Should be able to get all accounts until index', async () => { + const accounts = await ledgerService.getAccounts(transport, 6) + + expect(accounts.length).toBe(7) + accounts.forEach((account, index) => { + expect(account).toEqual( + expect.objectContaining({ + address: expect.any(String), + key: expect.any(String), + type: 'publicKey', + bip44Path: bsEthereum.bip44DerivationPath.replace('?', index.toString()), + }) + ) + }) + }, 60000) + it('Should be able to get account', async () => { const account = await ledgerService.getAccount(transport, 0) expect(account).toEqual( diff --git a/packages/bs-ethereum/src/services/ledger/EthersLedgerServiceEthereum.ts b/packages/bs-ethereum/src/services/ledger/EthersLedgerServiceEthereum.ts index eee8f49..c5da27a 100644 --- a/packages/bs-ethereum/src/services/ledger/EthersLedgerServiceEthereum.ts +++ b/packages/bs-ethereum/src/services/ledger/EthersLedgerServiceEthereum.ts @@ -4,6 +4,7 @@ import { LedgerServiceEmitter, fetchAccountsForBlockchainServices, GetLedgerTransport, + generateAccountUntilIndexForBlockchainService, } from '@cityofzion/blockchain-service' import Transport from '@ledgerhq/hw-transport' import LedgerEthereumApp, { ledgerService as LedgerEthereumAppService } from '@ledgerhq/hw-app-eth' @@ -161,13 +162,25 @@ export class EthersLedgerServiceEthereum impleme this.getLedgerTransport = getLedgerTransport } - async getAccounts(transport: Transport): Promise[]> { - const accountsByBlockchainService = await fetchAccountsForBlockchainServices( - [this.#blockchainService], - async (_service, index) => { - return this.getAccount(transport, index) - } - ) + async getAccounts(transport: Transport, untilIndex?: number): Promise[]> { + let accountsByBlockchainService: Map[]> + + if (untilIndex === undefined) { + accountsByBlockchainService = await fetchAccountsForBlockchainServices( + [this.#blockchainService], + async (_service, index) => { + return this.getAccount(transport, index) + } + ) + } else { + accountsByBlockchainService = await generateAccountUntilIndexForBlockchainService( + [this.#blockchainService], + untilIndex, + async (_service, index) => { + return this.getAccount(transport, index) + } + ) + } const accounts = accountsByBlockchainService.get(this.#blockchainService.name) return accounts ?? [] diff --git a/packages/bs-neo-legacy/src/__tests__/NeonJsLedgerServiceNeoLegacy.spec.ts b/packages/bs-neo-legacy/src/__tests__/NeonJsLedgerServiceNeoLegacy.spec.ts index 2f0e5a4..17f8066 100644 --- a/packages/bs-neo-legacy/src/__tests__/NeonJsLedgerServiceNeoLegacy.spec.ts +++ b/packages/bs-neo-legacy/src/__tests__/NeonJsLedgerServiceNeoLegacy.spec.ts @@ -17,7 +17,7 @@ describe.skip('NeonJsLedgerServiceNeoLegacy', () => { ledgerService = new NeonJsLedgerServiceNeoLegacy(bsNeoLegacy, async () => transport) }, 60000) - it('Should be able to get all accounts', async () => { + it('Should be able to get all accounts automatically', async () => { const accounts = await ledgerService.getAccounts(transport) expect(accounts.length).toBeGreaterThan(1) @@ -33,6 +33,22 @@ describe.skip('NeonJsLedgerServiceNeoLegacy', () => { }) }, 60000) + it('Should be able to get all accounts until index', async () => { + const accounts = await ledgerService.getAccounts(transport, 6) + + expect(accounts.length).toBe(7) + accounts.forEach((account, index) => { + expect(account).toEqual( + expect.objectContaining({ + address: expect.any(String), + key: expect.any(String), + type: 'publicKey', + bip44Path: bsNeoLegacy.bip44DerivationPath.replace('?', index.toString()), + }) + ) + }) + }, 60000) + it('Should be able to get account', async () => { const account = await ledgerService.getAccount(transport, 0) expect(account).toEqual( diff --git a/packages/bs-neo-legacy/src/services/ledger/NeonJsLedgerServiceNeoLegacy.ts b/packages/bs-neo-legacy/src/services/ledger/NeonJsLedgerServiceNeoLegacy.ts index fcbf6bf..9d80cc8 100644 --- a/packages/bs-neo-legacy/src/services/ledger/NeonJsLedgerServiceNeoLegacy.ts +++ b/packages/bs-neo-legacy/src/services/ledger/NeonJsLedgerServiceNeoLegacy.ts @@ -1,6 +1,7 @@ import { Account, fetchAccountsForBlockchainServices, + generateAccountUntilIndexForBlockchainService, GetLedgerTransport, LedgerService, LedgerServiceEmitter, @@ -59,13 +60,25 @@ export class NeonJsLedgerServiceNeoLegacy implem } } - async getAccounts(transport: Transport): Promise[]> { - const accountsByBlockchainService = await fetchAccountsForBlockchainServices( - [this.#blockchainService], - async (_service, index) => { - return this.getAccount(transport, index) - } - ) + async getAccounts(transport: Transport, untilIndex?: number): Promise[]> { + let accountsByBlockchainService: Map[]> + + if (untilIndex === undefined) { + accountsByBlockchainService = await fetchAccountsForBlockchainServices( + [this.#blockchainService], + async (_service, index) => { + return this.getAccount(transport, index) + } + ) + } else { + accountsByBlockchainService = await generateAccountUntilIndexForBlockchainService( + [this.#blockchainService], + untilIndex, + async (_service, index) => { + return this.getAccount(transport, index) + } + ) + } const accounts = accountsByBlockchainService.get(this.#blockchainService.name) return accounts ?? [] diff --git a/packages/bs-neo3/src/__tests__/services/ledger/NeonDappKitLedgerServiceNeo3.spec.ts b/packages/bs-neo3/src/__tests__/services/ledger/NeonDappKitLedgerServiceNeo3.spec.ts index 4f0e2c6..5379f17 100644 --- a/packages/bs-neo3/src/__tests__/services/ledger/NeonDappKitLedgerServiceNeo3.spec.ts +++ b/packages/bs-neo3/src/__tests__/services/ledger/NeonDappKitLedgerServiceNeo3.spec.ts @@ -4,9 +4,9 @@ import { BSNeo3 } from '../../../BSNeo3' import TransportNodeHid from '@ledgerhq/hw-transport-node-hid' import { BSNeo3Constants } from '../../../constants/BSNeo3Constants' -let ledgerService: NeonDappKitLedgerServiceNeo3 +let ledgerService: NeonDappKitLedgerServiceNeo3<'neo3'> let transport: Transport -let bsNeo3: BSNeo3 +let bsNeo3: BSNeo3<'neo3'> describe.skip('NeonDappKitLedgerServiceNeo3.spec', () => { beforeAll(async () => { @@ -17,7 +17,7 @@ describe.skip('NeonDappKitLedgerServiceNeo3.spec', () => { ledgerService = new NeonDappKitLedgerServiceNeo3(bsNeo3, async () => transport) }, 60000) - it('Should be able to get all accounts', async () => { + it('Should be able to get all accounts automatically', async () => { const accounts = await ledgerService.getAccounts(transport) expect(accounts.length).toBeGreaterThan(1) @@ -33,6 +33,22 @@ describe.skip('NeonDappKitLedgerServiceNeo3.spec', () => { }) }, 60000) + it('Should be able to get all accounts until index', async () => { + const accounts = await ledgerService.getAccounts(transport, 6) + + expect(accounts.length).toBe(7) + accounts.forEach((account, index) => { + expect(account).toEqual( + expect.objectContaining({ + address: expect.any(String), + key: expect.any(String), + type: 'publicKey', + bip44Path: bsNeo3.bip44DerivationPath.replace('?', index.toString()), + }) + ) + }) + }, 60000) + it('Should be able to get account', async () => { const account = await ledgerService.getAccount(transport, 0) expect(account).toEqual( diff --git a/packages/bs-neo3/src/services/ledger/NeonDappKitLedgerServiceNeo3.ts b/packages/bs-neo3/src/services/ledger/NeonDappKitLedgerServiceNeo3.ts index 79d38f6..7d4917c 100644 --- a/packages/bs-neo3/src/services/ledger/NeonDappKitLedgerServiceNeo3.ts +++ b/packages/bs-neo3/src/services/ledger/NeonDappKitLedgerServiceNeo3.ts @@ -1,6 +1,7 @@ import { Account, fetchAccountsForBlockchainServices, + generateAccountUntilIndexForBlockchainService, GetLedgerTransport, LedgerService, LedgerServiceEmitter, @@ -61,13 +62,25 @@ export class NeonDappKitLedgerServiceNeo3 implem } } - async getAccounts(transport: Transport): Promise[]> { - const accountsByBlockchainService = await fetchAccountsForBlockchainServices( - [this.#blockchainService], - async (_service, index) => { - return this.getAccount(transport, index) - } - ) + async getAccounts(transport: Transport, untilIndex?: number): Promise[]> { + let accountsByBlockchainService: Map[]> + + if (untilIndex === undefined) { + accountsByBlockchainService = await fetchAccountsForBlockchainServices( + [this.#blockchainService], + async (_service, index) => { + return this.getAccount(transport, index) + } + ) + } else { + accountsByBlockchainService = await generateAccountUntilIndexForBlockchainService( + [this.#blockchainService], + untilIndex, + async (_service, index) => { + return this.getAccount(transport, index) + } + ) + } const accounts = accountsByBlockchainService.get(this.#blockchainService.name) return accounts ?? []