Skip to content

Commit 4c3957a

Browse files
authored
Merge pull request #1568 from input-output-hk/feat/transaction-history-gap-issue
Feat/transaction history gap issue
2 parents b0c5b88 + f2a815f commit 4c3957a

File tree

2 files changed

+51
-9
lines changed

2 files changed

+51
-9
lines changed

packages/cardano-services-client/src/blockfrost/util.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,20 @@ export const fetchSequentially = async <Response, Item = Response>(
3232
haveEnoughItems?: (allItems: Item[], lastResponseBatch: Response[]) => boolean;
3333
paginationOptions?: PaginationOptions;
3434
},
35-
page = 1,
3635
accumulated: Item[] = []
3736
): Promise<Item[]> => {
3837
const count = props.paginationOptions?.count || 100;
38+
const page = props.paginationOptions?.page || 1;
3939
try {
4040
const response = await props.request(buildQueryString({ count, order: props.paginationOptions?.order, page }));
4141
const maybeTranslatedResponse = props.responseTranslator ? props.responseTranslator(response) : response;
4242
const newAccumulatedItems = [...accumulated, ...maybeTranslatedResponse] as Item[];
4343
const haveEnoughItems = props.haveEnoughItems?.(newAccumulatedItems, response);
4444
if (response.length === count && !haveEnoughItems) {
45-
return fetchSequentially<Response, Item>(props, page + 1, newAccumulatedItems);
45+
return fetchSequentially<Response, Item>(
46+
{ ...props, paginationOptions: { ...props.paginationOptions, page: page + 1 } },
47+
newAccumulatedItems
48+
);
4649
}
4750
return newAccumulatedItems;
4851
} catch (error) {

packages/wallet/src/services/TransactionsTracker.ts

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ import { newAndStoredMulticast } from './util/newAndStoredMulticast';
4040
import chunk from 'lodash/chunk.js';
4141
import sortBy from 'lodash/sortBy.js';
4242

43+
const ONE_MONTH_BLOCK_TIME = 21_600 * 6;
44+
4345
export interface TransactionsTrackerProps {
4446
chainHistoryProvider: ChainHistoryProvider;
4547
addresses$: Observable<Cardano.PaymentAddress[]>;
@@ -140,7 +142,7 @@ const allTransactionsByAddresses = async (
140142

141143
startAt += PAGE_SIZE;
142144
response = [...response, ...pageResults];
143-
} while (pageResults.length === PAGE_SIZE);
145+
} while (pageResults.length >= PAGE_SIZE);
144146
} else {
145147
const txes = await chainHistoryProvider.transactionsByAddresses({
146148
addresses: addressGroup,
@@ -201,6 +203,43 @@ export const revertLastBlock = (
201203
return deduplicateSortedArray(result, txEquals);
202204
};
203205

206+
/**
207+
* Fetches the last `historicalTransactionsFetchLimit` transactions for a set of addresses.
208+
* If there is a single address, it returns the most recent ones up to `historicalTransactionsFetchLimit`. If there
209+
* are more than one address, it returns all transaction from all addresses, one month back from the most recent transaction.
210+
*
211+
* @param {ChainHistoryProvider} chainHistoryProvider - The chain history provider used to fetch transaction history.
212+
* @param {Cardano.PaymentAddress[]} addresses - A list of Cardano payment addresses to fetch transactions for.
213+
* @param {number} historicalTransactionsFetchLimit - The maximum number of transactions to fetch in the initial pass.
214+
* @returns {Promise<Cardano.HydratedTx[]>} A promise that resolves to a list of hydrated transactions from the given addresses.
215+
*/
216+
const fetchInitialTransactions = async (
217+
chainHistoryProvider: ChainHistoryProvider,
218+
addresses: Cardano.PaymentAddress[],
219+
historicalTransactionsFetchLimit: number
220+
): Promise<Cardano.HydratedTx[]> => {
221+
const firstPassTxs = await allTransactionsByAddresses(chainHistoryProvider, {
222+
addresses,
223+
filterBy: { limit: historicalTransactionsFetchLimit, type: 'tip' }
224+
});
225+
226+
if (firstPassTxs.length === 0) {
227+
return [];
228+
}
229+
230+
if (addresses.length === 1) {
231+
return firstPassTxs;
232+
}
233+
234+
const highBlockNo = Cardano.BlockNo(Math.max(...firstPassTxs.map((tx) => tx.blockHeader.blockNo)));
235+
const onMonthBack = Cardano.BlockNo(Math.max(highBlockNo - ONE_MONTH_BLOCK_TIME, 0));
236+
237+
return await allTransactionsByAddresses(chainHistoryProvider, {
238+
addresses,
239+
filterBy: { blockRange: { lowerBound: onMonthBack }, type: 'blockRange' }
240+
});
241+
};
242+
204243
const findIntersectionAndUpdateTxStore = ({
205244
chainHistoryProvider,
206245
historicalTransactionsFetchLimit,
@@ -245,12 +284,12 @@ const findIntersectionAndUpdateTxStore = ({
245284
);
246285

247286
const lowerBound = lastStoredTransaction?.blockHeader.blockNo;
248-
const newTransactions = await allTransactionsByAddresses(chainHistoryProvider, {
249-
addresses,
250-
filterBy: lowerBound
251-
? { blockRange: { lowerBound }, type: 'blockRange' }
252-
: { limit: historicalTransactionsFetchLimit, type: 'tip' }
253-
});
287+
const newTransactions = await (lowerBound === undefined
288+
? fetchInitialTransactions(chainHistoryProvider, addresses, historicalTransactionsFetchLimit)
289+
: allTransactionsByAddresses(chainHistoryProvider, {
290+
addresses,
291+
filterBy: { blockRange: { lowerBound }, type: 'blockRange' }
292+
}));
254293

255294
logger.debug(
256295
`chainHistoryProvider returned ${newTransactions.length} transactions`,

0 commit comments

Comments
 (0)