diff --git a/src/events.ts b/src/events.ts index 47167899..d26a14ff 100644 --- a/src/events.ts +++ b/src/events.ts @@ -144,7 +144,7 @@ export const onBlock = async ( // fetch txs that include client's address with their utxo data const txs = await getTransactionsWithDetails( - Object.entries(txsCbor).map(([txid, cbor]) => ({ txid, cbor })), + Object.entries(txsCbor).map(([txId, cbor]) => ({ txId, cbor })), ); const notifications: TxNotification[] = []; diff --git a/src/methods/get-account-info.ts b/src/methods/get-account-info.ts index e1da03bb..033c81e6 100644 --- a/src/methods/get-account-info.ts +++ b/src/methods/get-account-info.ts @@ -116,7 +116,7 @@ export const getAccountInfo = async ( } else if (details === 'txs') { // fetch full transaction objects and set account.history.transactions const txs = await txIdsToTransactions( - requestedPageTxIds.map(item => ({ address: item.address, data: [item.tx_hash] })), + requestedPageTxIds.map(item => ({ address: item.address, txIds: [item.tx_hash] })), ); accountInfo.history.transactions = txs; diff --git a/src/methods/get-balance-history.ts b/src/methods/get-balance-history.ts index 9ce9206b..9e1417f8 100644 --- a/src/methods/get-balance-history.ts +++ b/src/methods/get-balance-history.ts @@ -136,7 +136,7 @@ export const getAccountBalanceHistory = async ( await txIdsToTransactions( txIds.map(tx => ({ address: tx.address, - data: [tx.tx_hash], + txIds: [tx.tx_hash], })), ) ) diff --git a/src/utils/address.ts b/src/utils/address.ts index 2e96675b..e8102b9c 100644 --- a/src/utils/address.ts +++ b/src/utils/address.ts @@ -8,7 +8,7 @@ import { } from '@blockfrost/blockfrost-js'; import memoizee from 'memoizee'; import { getAssetData, transformAsset } from './asset.js'; -import { assetMetadataLimiter, pLimiter } from './limiter.js'; +import { assetMetadataLimiter, limiter } from './limiter.js'; export const deriveAddress = ( publicKey: string, @@ -69,9 +69,7 @@ export const discoverAddresses = async ( ); addressCount++; - const promise = pLimiter.add(() => blockfrostAPI.addresses(address), { - throwOnTimeout: true, - }); + const promise = limiter(() => blockfrostAPI.addresses(address)); promisesBundle.push({ address, promise, path }); } @@ -113,17 +111,15 @@ export const addressesToUtxos = async ( const promises = addresses.map(item => item.data === 'empty' ? [] - : pLimiter.add( - () => - // change batchSize to fetch only 1 page at a time (each page has 100 utxos) - blockfrostAPI.addressesUtxosAll(item.address, { batchSize: 1 }).catch(error => { - if (error instanceof BlockfrostServerError && error.status_code === 404) { - return []; - } else { - throw error; - } - }), - { throwOnTimeout: true }, + : limiter(() => + // change batchSize to fetch only 1 page at a time (each page has 100 utxos) + blockfrostAPI.addressesUtxosAll(item.address, { batchSize: 1 }).catch(error => { + if (error instanceof BlockfrostServerError && error.status_code === 404) { + return []; + } else { + throw error; + } + }), ), ); @@ -167,15 +163,13 @@ export const utxosWithBlocks = async ( if (utxo.data === 'empty') continue; for (const utxoData of utxo.data) { - const promise = pLimiter.add( - () => - blockfrostAPI.blocks(utxoData.block).then(blockData => ({ - address: utxo.address, - path: utxo.path, - utxoData: utxoData, - blockInfo: blockData, - })), - { throwOnTimeout: true }, + const promise = limiter(() => + blockfrostAPI.blocks(utxoData.block).then(blockData => ({ + address: utxo.address, + path: utxo.path, + utxoData: utxoData, + blockInfo: blockData, + })), ); promisesBundle.push(promise); @@ -198,27 +192,25 @@ export const addressesToTxIds = async ( for (const item of addresses) { if (item.data === 'empty') continue; - const promise = pLimiter.add( - () => - // 1 page (100 txs) per address at a time should be more efficient default value - // compared to fetching 10 pages (1000 txs) per address - blockfrostAPI - .addressesTransactionsAll(item.address, { batchSize: 1 }) - .then(data => ({ - address: item.address, - data, - })) - .catch(error => { - if (error instanceof BlockfrostServerError && error.status_code === 404) { - return { - address: item.address, - data: [], - }; - } else { - throw error; - } - }), - { throwOnTimeout: true }, + const promise = limiter(() => + // 1 page (100 txs) per address at a time should be more efficient default value + // compared to fetching 10 pages (1000 txs) per address + blockfrostAPI + .addressesTransactionsAll(item.address, { batchSize: 1 }) + .then(data => ({ + address: item.address, + data, + })) + .catch(error => { + if (error instanceof BlockfrostServerError && error.status_code === 404) { + return { + address: item.address, + data: [], + }; + } else { + throw error; + } + }), ); promisesBundle.push(promise); @@ -244,22 +236,20 @@ export const getAddressesData = async ( } const promises = addresses.map(addr => - pLimiter.add( - () => - blockfrostAPI.addressesTotal(addr.address).catch(error => { - if (error.status_code === 404) { - return { - address: addr.address, - path: addr.path, - tx_count: 0, - received_sum: [{ unit: 'lovelace', quantity: '0' }], - sent_sum: [{ unit: 'lovelace', quantity: '0' }], - }; - } else { - throw new Error(error); - } - }), - { throwOnTimeout: true }, + limiter(() => + blockfrostAPI.addressesTotal(addr.address).catch(error => { + if (error.status_code === 404) { + return { + address: addr.address, + path: addr.path, + tx_count: 0, + received_sum: [{ unit: 'lovelace', quantity: '0' }], + sent_sum: [{ unit: 'lovelace', quantity: '0' }], + }; + } else { + throw new Error(error); + } + }), ), ); const responses = await Promise.all(promises); diff --git a/src/utils/limiter.ts b/src/utils/limiter.ts index 6025d1fb..d47c23cf 100644 --- a/src/utils/limiter.ts +++ b/src/utils/limiter.ts @@ -28,3 +28,6 @@ pLimiter.on('error', error => { ratesLimiter.on('error', error => { logger.warn(`ratesLimiter error`, error); }); + +export const limiter = (task: () => PromiseLike) => + pLimiter.add(task, { throwOnTimeout: true }); diff --git a/src/utils/transaction.ts b/src/utils/transaction.ts index 7a5577fa..25affdad 100644 --- a/src/utils/transaction.ts +++ b/src/utils/transaction.ts @@ -3,7 +3,7 @@ import * as Types from '../types/transactions.js'; import { TxIdsToTransactionsResponse } from '../types/transactions.js'; import { blockfrostAPI } from '../utils/blockfrost-api.js'; import { getAssetData, transformAsset } from './asset.js'; -import { assetMetadataLimiter, pLimiter } from './limiter.js'; +import { assetMetadataLimiter, limiter } from './limiter.js'; import { logger } from './logger.js'; export const sortTransactionsCmp = < @@ -23,12 +23,7 @@ const fetchTxWithUtxo = async (txHash: string, address: string) => { const txData = await transformTransactionData(tx); const txUtxos = await transformTransactionUtxo(txUtxo); - return { - txData, - txUtxos, - address: address, - txHash: txHash, - }; + return { txData, txUtxos, address, txHash }; } catch (error) { // WARNING: this will omit txs that returned 404, caller should be well aware of this fact if (error instanceof BlockfrostServerError && error.status_code === 404) { @@ -43,7 +38,7 @@ const fetchTxWithUtxo = async (txHash: string, address: string) => { export const txIdsToTransactions = async ( txidsPerAddress: { address: string; - data: string[]; + txIds: string[]; }[], ): Promise => { if (txidsPerAddress.length === 0) return []; @@ -51,10 +46,8 @@ export const txIdsToTransactions = async ( const promises: Promise[] = []; for (const item of txidsPerAddress) { - for (const tx of item.data) { - promises.push( - pLimiter.add(() => fetchTxWithUtxo(tx, item.address), { throwOnTimeout: true }), - ); + for (const txId of item.txIds) { + promises.push(limiter(() => fetchTxWithUtxo(txId, item.address))); } } @@ -69,7 +62,7 @@ export const txIdsToTransactions = async ( }; export interface GetTransactionsDetails { - txid: string; + txId: string; cbor?: boolean; } @@ -77,26 +70,19 @@ export const getTransactionsWithDetails = async ( txs: GetTransactionsDetails[], ): Promise[]> => { const txsData = await Promise.all( - txs.map(({ txid }) => - pLimiter.add(() => blockfrostAPI.txs(txid).then(data => transformTransactionData(data)), { - throwOnTimeout: true, - }), + txs.map(({ txId }) => + limiter(() => blockfrostAPI.txs(txId).then(data => transformTransactionData(data))), ), ); const txsUtxo = await Promise.all( - txs.map(({ txid }) => - pLimiter.add( - () => blockfrostAPI.txsUtxos(txid).then(data => transformTransactionUtxo(data)), - { throwOnTimeout: true }, - ), + txs.map(({ txId }) => + limiter(() => blockfrostAPI.txsUtxos(txId).then(data => transformTransactionUtxo(data))), ), ); const txsCbors = await Promise.all( - txs.map(({ txid, cbor }) => + txs.map(({ txId, cbor }) => cbor - ? pLimiter.add(() => blockfrostAPI.txsCbor(txid).then(data => data.cbor), { - throwOnTimeout: true, - }) + ? limiter(() => blockfrostAPI.txsCbor(txId).then(data => data.cbor)) : // eslint-disable-next-line unicorn/no-useless-undefined Promise.resolve(undefined), ), diff --git a/test/unit/tests/utils/events.test.ts b/test/unit/tests/utils/events.test.ts index 68a2898a..5daca410 100644 --- a/test/unit/tests/utils/events.test.ts +++ b/test/unit/tests/utils/events.test.ts @@ -153,7 +153,7 @@ describe('events', () => { return new Promise(resolve => { // sanity check that the test really wanted to fetch transactions that we expected for (const mockedTx of fixture.mocks.txsWithUtxo) { - if (!txs.find(({ txid }) => mockedTx.txData.hash === txid)) { + if (!txs.find(({ txId }) => mockedTx.txData.hash === txId)) { throw new Error('Unexpected list of affected addresses'); } }