Skip to content

Commit 2b9d915

Browse files
committed
fix(cardano-services-client)!: return up to limit*addresses.length # of transactions
BlockfrostChainHistoryProvider used to incorrectly slice all-address transactions array. This commit changes the behavior to apply the limit per address in order to not miss any transactions. The more addresses you have, the higher the limit.
1 parent 347a577 commit 2b9d915

File tree

2 files changed

+63
-31
lines changed

2 files changed

+63
-31
lines changed

packages/cardano-services-client/src/ChainHistoryProvider/BlockfrostChainHistoryProvider.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -522,17 +522,15 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
522522

523523
const allTransactions = addressTransactions.flat(1);
524524

525-
const dedupedSortedTransactions = uniq(
525+
const dedupedSortedTransactionsIds = uniq(
526526
allTransactions
527527
.filter(({ block_height }) => block_height >= lowerBound && block_height <= upperBound)
528528
.sort(pagination.order === 'desc' ? (a, b) => compareTx(b, a) : compareTx)
529529
.map(({ tx_hash }) => Cardano.TransactionId(tx_hash))
530530
);
531-
const ids = dedupedSortedTransactions.slice(pagination.startAt, pagination.limit);
531+
const pageResults = await this.transactionsByHashes({ ids: dedupedSortedTransactionsIds });
532532

533-
const pageResults = await this.transactionsByHashes({ ids });
534-
535-
return { pageResults, totalResultCount: dedupedSortedTransactions.length };
533+
return { pageResults, totalResultCount: dedupedSortedTransactionsIds.length };
536534
} catch (error) {
537535
throw this.toProviderError(error);
538536
}

packages/cardano-services-client/test/ChainHistoryProvider/BlockfrostChainHistoryProvider.test.ts

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,14 @@ describe('blockfrostChainHistoryProvider', () => {
1111
let provider: BlockfrostChainHistoryProvider;
1212
let networkInfoProvider: NetworkInfoProvider;
1313

14+
const txId1 = Cardano.TransactionId('1e043f100dce12d107f679685acd2fc0610e10f72a92d412794c9773d11d8477');
15+
const txId2 = Cardano.TransactionId('2e043f100dce12d107f679685acd2fc0610e10f72a92d412794c9773d11d8477');
16+
const address1 = Cardano.PaymentAddress('2cWKMJemoBai9J7kVvRTukMmdfxtjL9z7c396rTfrrzfAZ6EeQoKLC2y1k34hswwm4SVr');
17+
const address2 = Cardano.PaymentAddress(
18+
'addr_test1qra788mu4sg8kwd93ns9nfdh3k4ufxwg4xhz2r3n064tzfgxu2hyfhlkwuxupa9d5085eunq2qywy7hvmvej456flkns6cy45x'
19+
);
1420
const txsUtxosResponse = {
15-
hash: '4123d70f66414cc921f6ffc29a899aafc7137a99a0fd453d6b200863ef5702d6',
21+
hash: txId1,
1622
inputs: [
1723
{
1824
address:
@@ -58,13 +64,13 @@ describe('blockfrostChainHistoryProvider', () => {
5864
}
5965
]
6066
};
61-
const mockedTxResponse = {
67+
const mockedTx1Response = {
6268
asset_mint_or_burn_count: 5,
6369
block: '356b7d7dbb696ccd12775c016941057a9dc70898d87a63fc752271bb46856940',
6470
block_height: 123_456,
6571
delegation_count: 0,
6672
fees: '182485',
67-
hash: '1e043f100dce12d107f679685acd2fc0610e10f72a92d412794c9773d11d8477',
73+
hash: txId1,
6874
index: 1,
6975
invalid_before: null,
7076
invalid_hereafter: '13885913',
@@ -89,6 +95,10 @@ describe('blockfrostChainHistoryProvider', () => {
8995
valid_contract: true,
9096
withdrawal_count: 1
9197
};
98+
const mockedTx2Response = {
99+
...mockedTx1Response,
100+
hash: txId2
101+
};
92102
const mockedMetadataResponse = [
93103
{
94104
json_metadata: {
@@ -186,11 +196,20 @@ describe('blockfrostChainHistoryProvider', () => {
186196
unit_steps: '476468'
187197
}
188198
];
199+
189200
const mockedAddressTransactionResponse: Responses['address_transactions_content'] = [
190201
{
191202
block_height: 123,
192203
block_time: 131_322,
193-
tx_hash: '1e043f100dce12d107f679685acd2fc0610e10f72a92d412794c9773d11d8477',
204+
tx_hash: txId1,
205+
tx_index: 0
206+
}
207+
];
208+
const mockedAddress2TransactionResponse: Responses['address_transactions_content'] = [
209+
{
210+
block_height: 124,
211+
block_time: 131_322,
212+
tx_hash: txId2,
194213
tx_index: 0
195214
}
196215
];
@@ -352,25 +371,30 @@ describe('blockfrostChainHistoryProvider', () => {
352371
])
353372
} as unknown as NetworkInfoProvider;
354373
provider = new BlockfrostChainHistoryProvider(client, networkInfoProvider, logger);
355-
const txId = Cardano.TransactionId('1e043f100dce12d107f679685acd2fc0610e10f72a92d412794c9773d11d8477');
356-
const id = txId.toString();
357374
mockResponses(request, [
358-
[`txs/${id}/utxos`, txsUtxosResponse],
359-
[`txs/${id}`, mockedTxResponse],
360-
[`txs/${id}/metadata`, mockedMetadataResponse],
361-
[`txs/${id}/mirs`, mockedMirResponse],
362-
[`txs/${id}/pool_updates`, mockedPoolUpdateResponse],
363-
[`txs/${id}/pool_retires`, mockedPoolRetireResponse],
364-
[`txs/${id}/stakes`, mockedStakeResponse],
365-
[`txs/${id}/delegations`, mockedDelegationResponse],
366-
[`txs/${id}/withdrawals`, mockedWithdrawalResponse],
367-
[`txs/${id}/redeemers`, mockedReedemerResponse],
368-
[
369-
`addresses/${Cardano.PaymentAddress(
370-
'2cWKMJemoBai9J7kVvRTukMmdfxtjL9z7c396rTfrrzfAZ6EeQoKLC2y1k34hswwm4SVr'
371-
).toString()}/transactions?page=1&count=20`,
372-
mockedAddressTransactionResponse
373-
],
375+
[`txs/${txId1}/utxos`, txsUtxosResponse],
376+
[`txs/${txId1}`, mockedTx1Response],
377+
[`txs/${txId1}/metadata`, mockedMetadataResponse],
378+
[`txs/${txId1}/mirs`, mockedMirResponse],
379+
[`txs/${txId1}/pool_updates`, mockedPoolUpdateResponse],
380+
[`txs/${txId1}/pool_retires`, mockedPoolRetireResponse],
381+
[`txs/${txId1}/stakes`, mockedStakeResponse],
382+
[`txs/${txId1}/delegations`, mockedDelegationResponse],
383+
[`txs/${txId1}/withdrawals`, mockedWithdrawalResponse],
384+
[`txs/${txId1}/redeemers`, mockedReedemerResponse],
385+
[`txs/${txId2}/utxos`, txsUtxosResponse],
386+
[`txs/${txId2}`, mockedTx2Response],
387+
[`txs/${txId2}/metadata`, mockedMetadataResponse],
388+
[`txs/${txId2}/mirs`, mockedMirResponse],
389+
[`txs/${txId2}/pool_updates`, mockedPoolUpdateResponse],
390+
[`txs/${txId2}/pool_retires`, mockedPoolRetireResponse],
391+
[`txs/${txId2}/stakes`, mockedStakeResponse],
392+
[`txs/${txId2}/delegations`, mockedDelegationResponse],
393+
[`txs/${txId2}/withdrawals`, mockedWithdrawalResponse],
394+
[`txs/${txId2}/redeemers`, mockedReedemerResponse],
395+
[`addresses/${address1}/transactions?page=1&count=20`, mockedAddressTransactionResponse],
396+
[`addresses/${address1}/transactions?page=1&count=1`, mockedAddressTransactionResponse],
397+
[`addresses/${address2}/transactions?page=1&count=1`, mockedAddress2TransactionResponse],
374398
[
375399
`addresses/${Cardano.PaymentAddress(
376400
'addr_test1qra788mu4sg8kwd93ns9nfdh3k4ufxwg4xhz2r3n064tzfgxu2hyfhlkwuxupa9d5085eunq2qywy7hvmvej456flkns6cy45x'
@@ -382,7 +406,7 @@ describe('blockfrostChainHistoryProvider', () => {
382406
mockedAddressTransactionDescResponse
383407
],
384408
['epochs/420000/parameters', mockedEpochParametersResponse],
385-
[`txs/${id}/cbor`, new Error('CBOR is null')]
409+
[`txs/${txId1}/cbor`, new Error('CBOR is null')]
386410
]);
387411
});
388412

@@ -394,7 +418,7 @@ describe('blockfrostChainHistoryProvider', () => {
394418
pagination: { limit: 20, startAt: 0 }
395419
});
396420

397-
expect(response.totalResultCount).toBe(1);
421+
expect(response.totalResultCount).toBe(mockedAddressTransactionResponse.length);
398422
expect(response.pageResults[0]).toEqual(expectedHydratedTx);
399423
});
400424
test('supports desc order', async () => {
@@ -418,6 +442,16 @@ describe('blockfrostChainHistoryProvider', () => {
418442
expect(response.pageResults).toHaveLength(mockedAddressTransactionResponse.length);
419443
expect(response.totalResultCount).toBe(mockedAddressTransactionResponse.length);
420444
});
445+
test('returns up to the {limit*addresses.length} number of transactions', async () => {
446+
const response = await provider.transactionsByAddresses({
447+
addresses: [address1, address2],
448+
pagination: { limit: 1, startAt: 0 }
449+
});
450+
451+
const totalResultCount = mockedAddressTransactionResponse.length + mockedAddress2TransactionResponse.length;
452+
expect(response.totalResultCount).toBe(totalResultCount);
453+
expect(response.pageResults.length).toBe(totalResultCount);
454+
});
421455
});
422456

423457
describe('transactionsByHashes', () => {
@@ -514,7 +548,7 @@ describe('blockfrostChainHistoryProvider', () => {
514548
const id = txId.toString();
515549
mockResponses(request, [
516550
[`txs/${id}/utxos`, txsUtxosResponse],
517-
[`txs/${id}`, mockedTxResponse],
551+
[`txs/${id}`, mockedTx1Response],
518552
[`txs/${id}/metadata`, mockedMetadataResponse],
519553
[`txs/${id}/mirs`, mockedMirResponse],
520554
[`txs/${id}/pool_updates`, mockedPoolUpdateResponse],
@@ -541,7 +575,7 @@ describe('blockfrostChainHistoryProvider', () => {
541575
pagination: { limit: 20, startAt: 0 }
542576
});
543577

544-
expect(response.totalResultCount).toBe(1);
578+
expect(response.totalResultCount).toBe(mockedAddressTransactionResponse.length);
545579
expect(response.pageResults[0]).toEqual(expectedHydratedTxCBOR);
546580
});
547581
});

0 commit comments

Comments
 (0)