Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add mempool support to GET_ACCOUNT_INFO txids #284

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading