Skip to content

Commit

Permalink
feat: add mempool support to GET_ACCOUNT_INFO txids
Browse files Browse the repository at this point in the history
  • Loading branch information
iccicci committed Jan 9, 2025
1 parent 04471fb commit 1192011
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 39 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,11 @@ Input message:
"command": "GET_ACCOUNT_INFO";
"params": {
"descriptor": string; // account public key in hex (eg. 6d17587575a3b4f0f86ebad3977e8f7e4981faa863eccf5c1467065c74fe3435943769446dd290d103fb3d360128e86de4b47faea73ffb0900c94c6a61ef9ea2)
"details": 'basic' | 'tokens' | 'tokenBalances' |'txids' | 'txs';
"details": 'basic' | 'tokens' | 'tokenBalances' | 'txids' | 'txs';
"page"?: number; // optional, default 1
"pageSize"?: number; // optional, default 20
"cbor"?: boolean; // optional, get CBOR representation of transactions
"mempool"?: boolean; // optional, get info from mempool as well - WIP - works only with details 'txids'
}
}
```
Expand Down
6 changes: 4 additions & 2 deletions src/methods/get-account-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const getAccountInfo = async (
page = 1,
pageSize = 25,
cbor?: boolean,
mempool?: boolean,
): Promise<Responses.AccountInfo> => {
let _addressesCount = 0;
const tStart = Date.now();
Expand Down Expand Up @@ -104,7 +105,7 @@ export const getAccountInfo = async (

_addressesCount = addresses.length; // just a debug helper

const txids = await getTxidsFromAccountAddresses(addresses, accountEmpty);
const txids = await getTxidsFromAccountAddresses(addresses, accountEmpty, mempool);
const paginatedTxsIds = paginate(txids, pageSizeNumber);
const requestedPageTxIds = paginatedTxsIds[pageIndex] ?? [];

Expand Down Expand Up @@ -157,8 +158,9 @@ export default async (
page = 1,
pageSize = 25,
cbor?: boolean,
mempool?: boolean,
): Promise<string> => {
const data = await getAccountInfo(publicKey, details, page, pageSize, cbor);
const data = await getAccountInfo(publicKey, details, page, pageSize, cbor, mempool);
const message = prepareMessage({ id, clientId, data });

return message;
Expand Down
1 change: 1 addition & 0 deletions src/types/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type Messages = BaseMessage &
page?: number;
pageSize?: number;
cbor?: boolean;
mempool?: boolean;
};
}
| {
Expand Down
32 changes: 24 additions & 8 deletions src/utils/account.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import { Address } from '../types/address.js';
import { Responses } from '@blockfrost/blockfrost-js';
import { addressesToTxIds, discoverAddresses, getAddressesData } from './address.js';
import {
AddressesToTxIds,
addressesToTxIds,
discoverAddresses,
getAddressesData,
} from './address.js';

export const getTxidsFromAccountAddresses = async (addresses: Address[], accountEmpty: boolean) => {
export const getTxidsFromAccountAddresses = async (
addresses: Address[],
accountEmpty: boolean,
mempool?: boolean,
) => {
const uniqueTxIds: ({
address: string;
} & Responses['address_transactions_content'][number])[] = [];
} & AddressesToTxIds[number]['data'][number])[] = [];

if (accountEmpty) {
return [];
}

const transactionsPerAddressList = await addressesToTxIds(addresses);
const transactionsPerAddressList = await addressesToTxIds(addresses, mempool);

// filter only unique txs to prevent counting a transaction that was sent from and to the same account twice
for (const txsPerAddress of transactionsPerAddressList) {
Expand All @@ -22,9 +30,17 @@ export const getTxidsFromAccountAddresses = async (addresses: Address[], account
}
}

const sortedTxIds = uniqueTxIds.sort(
(first, second) => second.block_height - first.block_height || second.tx_index - first.tx_index,
);
const sortedTxIds = uniqueTxIds.sort((first, second) => {
if (first.mempool) {
return -1;
}

if (second.mempool) {
return 1;
}

return second.block_height - first.block_height || second.tx_index - first.tx_index;
});

return sortedTxIds;
};
Expand Down
88 changes: 60 additions & 28 deletions src/utils/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,40 +183,72 @@ export const utxosWithBlocks = async (
return result;
};

export type AddressesToTxIds = {
address: string;
data: (
| (Responses['address_transactions_content'][number] & { mempool?: boolean })
| { tx_hash: string; mempool: true }
)[];
}[];

export const addressesToTxIds = async (
addresses: Addresses.Address[],
): Promise<{ address: string; data: Responses['address_transactions_content'] }[]> => {
const promisesBundle: Promise<{
address: string;
data: Responses['address_transactions_content'];
}>[] = [];

for (const { address, data: status } of addresses) {
if (status === 'empty') {
continue;
}

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(address, { batchSize: 1 })
.then(data => ({ address, data }))
.catch(error => {
if (error instanceof BlockfrostServerError && error.status_code === 404) {
return { address, data: [] };
} else {
throw error;
}
}),
);
mempool?: boolean,
): Promise<AddressesToTxIds> => {
const result = await Promise.all(
addresses
.filter(({ data }) => data !== 'empty')
.map(({ address }) =>
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(address, { batchSize: 1 })
.then(data => ({ address, data }))
.catch(error => {
if (error instanceof BlockfrostServerError && error.status_code === 404) {
return { address, data: [] };
} else {
throw error;
}
}),
),
),
);

promisesBundle.push(promise);
if (!mempool) {
return result;
}

const result = await Promise.all(promisesBundle);
// Fetch the txids list from mempool
const mempoolResult = (await Promise.all(
addresses.map(({ address }) =>
limiter(() =>
blockfrostAPI
.mempoolByAddress(address)
.then(data => ({ address, data: data.map(element => ({ ...element, mempool: true })) }))
.catch(error => {
if (error instanceof BlockfrostServerError && error.status_code === 404) {
return { address, data: [] };
} else {
throw error;
}
}),
),
),
)) as AddressesToTxIds;

return result;
let resultIdx = 0;

for (const element of mempoolResult) {
// If the address is empty, result do not contains an entry for it
if (result[resultIdx].address === element.address) {
element.data = [...element.data, ...result[resultIdx].data];
resultIdx++;
}
}

return mempoolResult;
};

export const getAddressesData = async (
Expand Down

0 comments on commit 1192011

Please sign in to comment.