Skip to content

Commit 7ce4bc4

Browse files
committed
fixup! feat(cardano-services): implement missing Blockfrost providers features and tests
1 parent 1e29a18 commit 7ce4bc4

File tree

15 files changed

+562
-207
lines changed

15 files changed

+562
-207
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ describe('chainHistoryProvider', () => {
99
describe('healthCheck', () => {
1010
it('is not ok if cannot connect', async () => {
1111
const provider = chainHistoryHttpProvider(config);
12-
await expect(() => provider.healthCheck()).rejects.toThrow();
12+
await expect(async () => await provider.healthCheck()).rejects.toThrow();
1313
});
1414
});
1515
describe('mocked', () => {

packages/cardano-services-client/test/TxSubmitProvider/txSubmitHttpProvider.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ describe('txSubmitHttpProvider', () => {
1919
describe('healthCheck', () => {
2020
it('is not ok if cannot connect', async () => {
2121
const provider = txSubmitHttpProvider(config);
22-
await expect(() => provider.healthCheck()).rejects.toThrow();
22+
await expect(async () => await provider.healthCheck()).rejects.toThrow();
2323
});
2424
});
2525
// eslint-disable-next-line sonarjs/cognitive-complexity

packages/cardano-services/src/Asset/BlockfrostAssetProvider/BlockfrostAssetProvider.ts

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { Asset, AssetProvider, Cardano, GetAssetArgs, GetAssetsArgs } from '@cardano-sdk/core';
33
import { BlockFrostAPI, Responses } from '@blockfrost/blockfrost-js';
44
import { BlockfrostProvider } from '../../util/BlockfrostProvider/BlockfrostProvider';
5-
import { blockfrostMetadataToTxMetadata, fetchSequentially } from '../../util';
5+
import { blockfrostMetadataToTxMetadata, blockfrostToProviderError, fetchSequentially } from '../../util';
66
import { replaceNullsWithUndefineds } from '@cardano-sdk/util';
77

88
export class BlockfrostAssetProvider extends BlockfrostProvider implements AssetProvider {
@@ -48,32 +48,36 @@ export class BlockfrostAssetProvider extends BlockfrostProvider implements Asset
4848
}
4949
// getAsset: (args: GetAssetArgs) => Promise<Asset.AssetInfo>;
5050
async getAsset({ assetId, extraData }: GetAssetArgs) {
51-
const response = await this.blockfrost.assetsById(assetId.toString());
52-
const name = Cardano.AssetId.getAssetName(assetId);
53-
const policyId = Cardano.PolicyId(response.policy_id);
54-
const quantity = BigInt(response.quantity);
51+
try {
52+
const response = await this.blockfrost.assetsById(assetId.toString());
53+
const name = Cardano.AssetId.getAssetName(assetId);
54+
const policyId = Cardano.PolicyId(response.policy_id);
55+
const quantity = BigInt(response.quantity);
5556

56-
const nftMetadata = async () => {
57-
let lastMintedTxHash: string = response.initial_mint_tx_hash;
58-
if (response.mint_or_burn_count > 1) {
59-
const lastMintedTx = await this.getLastMintedTx(assetId);
60-
if (lastMintedTx) lastMintedTxHash = lastMintedTx.tx_hash;
61-
}
62-
return this.getNftMetadata({ name, policyId }, lastMintedTxHash);
63-
};
57+
const nftMetadata = async () => {
58+
let lastMintedTxHash: string = response.initial_mint_tx_hash;
59+
if (response.mint_or_burn_count > 1) {
60+
const lastMintedTx = await this.getLastMintedTx(assetId);
61+
if (lastMintedTx) lastMintedTxHash = lastMintedTx.tx_hash;
62+
}
63+
return this.getNftMetadata({ name, policyId }, lastMintedTxHash);
64+
};
6465

65-
return {
66-
assetId,
67-
fingerprint: Cardano.AssetFingerprint(response.fingerprint),
68-
// history: extraData?.history ? await history() : undefined,
69-
mintOrBurnCount: response.mint_or_burn_count,
70-
name,
71-
nftMetadata: extraData?.nftMetadata ? await nftMetadata() : undefined,
72-
policyId,
73-
quantity,
74-
supply: quantity,
75-
tokenMetadata: extraData?.tokenMetadata ? this.mapMetadata(assetId, response.metadata) : undefined
76-
};
66+
return {
67+
assetId,
68+
fingerprint: Cardano.AssetFingerprint(response.fingerprint),
69+
// history: extraData?.history ? await history() : undefined,
70+
mintOrBurnCount: response.mint_or_burn_count,
71+
name,
72+
nftMetadata: extraData?.nftMetadata ? await nftMetadata() : undefined,
73+
policyId,
74+
quantity,
75+
supply: quantity,
76+
tokenMetadata: extraData?.tokenMetadata ? this.mapMetadata(assetId, response.metadata) : undefined
77+
};
78+
} catch (error) {
79+
throw blockfrostToProviderError(error);
80+
}
7781
}
7882
async getAssets({ assetIds, extraData }: GetAssetsArgs) {
7983
return Promise.all(assetIds.map((assetId) => this.getAsset({ assetId, extraData })));

packages/cardano-services/src/ChainHistory/BlockrostChainHistoryProvider/BlockfrostChainHistoryProvider.ts

Lines changed: 107 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
BlockfrostToCore,
66
BlockfrostTransactionContent,
77
blockfrostMetadataToTxMetadata,
8+
blockfrostToProviderError,
89
fetchByAddressSequentially,
910
isBlockfrostNotFoundError
1011
} from '../../util';
@@ -185,111 +186,128 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
185186
protected parseValidityInterval = (num: string | null) => Cardano.Slot(Number.parseInt(num || '')) || undefined;
186187

187188
protected async fetchTransaction(hash: Cardano.TransactionId): Promise<Cardano.HydratedTx> {
188-
const utxos: Responses['tx_content_utxo'] = await this.blockfrost.txsUtxos(hash.toString());
189-
const { inputs, outputs, collaterals } = BlockfrostToCore.transactionUtxos(utxos);
189+
try {
190+
const utxos: Responses['tx_content_utxo'] = await this.blockfrost.txsUtxos(hash.toString());
191+
const { inputs, outputs, collaterals } = BlockfrostToCore.transactionUtxos(utxos);
190192

191-
const response = await this.blockfrost.txs(hash.toString());
192-
const metadata = await this.fetchJsonMetadata(hash);
193-
const certificates = await this.fetchCertificates(response);
194-
const withdrawals = await this.fetchWithdrawals(response);
195-
return {
196-
auxiliaryData: metadata
197-
? {
198-
blob: metadata
199-
}
200-
: undefined,
193+
const response = await this.blockfrost.txs(hash.toString());
194+
const metadata = await this.fetchJsonMetadata(hash);
195+
const certificates = await this.fetchCertificates(response);
196+
const withdrawals = await this.fetchWithdrawals(response);
197+
return {
198+
auxiliaryData: metadata
199+
? {
200+
blob: metadata
201+
}
202+
: undefined,
201203

202-
blockHeader: {
203-
blockNo: Cardano.BlockNo(response.block_height),
204-
hash: Cardano.BlockId(response.block),
205-
slot: Cardano.Slot(response.slot)
206-
},
207-
body: {
208-
certificates,
209-
collaterals,
210-
fee: BigInt(response.fees),
211-
inputs,
212-
mint: this.gatherMintsFromUtxos(response, utxos),
213-
outputs,
214-
validityInterval: {
215-
invalidBefore: this.parseValidityInterval(response.invalid_before),
216-
invalidHereafter: this.parseValidityInterval(response.invalid_hereafter)
204+
blockHeader: {
205+
blockNo: Cardano.BlockNo(response.block_height),
206+
hash: Cardano.BlockId(response.block),
207+
slot: Cardano.Slot(response.slot)
217208
},
218-
withdrawals
219-
},
220-
id: hash,
221-
index: response.index,
222-
inputSource: inputs && inputs.length > 0 ? Cardano.InputSource.inputs : Cardano.InputSource.collaterals,
223-
txSize: response.size,
224-
witness: {
225-
redeemers: await this.fetchRedeemers(response),
226-
signatures: new Map() // not available in blockfrost @todo check again
227-
}
228-
};
209+
body: {
210+
certificates,
211+
collaterals,
212+
fee: BigInt(response.fees),
213+
inputs,
214+
mint: this.gatherMintsFromUtxos(response, utxos),
215+
outputs,
216+
validityInterval: {
217+
invalidBefore: this.parseValidityInterval(response.invalid_before),
218+
invalidHereafter: this.parseValidityInterval(response.invalid_hereafter)
219+
},
220+
withdrawals
221+
},
222+
id: hash,
223+
index: response.index,
224+
inputSource: inputs && inputs.length > 0 ? Cardano.InputSource.inputs : Cardano.InputSource.collaterals,
225+
txSize: response.size,
226+
witness: {
227+
redeemers: await this.fetchRedeemers(response),
228+
signatures: new Map() // not available in blockfrost @todo check again
229+
}
230+
};
231+
} catch (error) {
232+
throw blockfrostToProviderError(error);
233+
}
229234
}
230235

231236
public async blocksByHashes({ ids }: BlocksByIdsArgs): Promise<Cardano.ExtendedBlockInfo[]> {
232-
const responses = await Promise.all(ids.map((id) => this.blockfrost.blocks(id.toString())));
233-
return responses.map((response) => {
234-
if (!response.epoch || !response.epoch_slot || !response.height || !response.slot || !response.block_vrf) {
235-
throw new ProviderError(ProviderFailure.Unknown, null, 'Queried unsupported block');
236-
}
237-
return {
238-
confirmations: response.confirmations,
239-
date: new Date(response.time * 1000),
240-
epoch: Cardano.EpochNo(response.epoch),
241-
epochSlot: response.epoch_slot,
242-
fees: BigInt(response.fees || '0'),
243-
header: {
244-
blockNo: Cardano.BlockNo(response.height),
245-
hash: Cardano.BlockId(response.hash),
246-
slot: Cardano.Slot(response.slot)
247-
},
248-
nextBlock: response.next_block ? Cardano.BlockId(response.next_block) : undefined,
249-
previousBlock: response.previous_block ? Cardano.BlockId(response.previous_block) : undefined,
250-
size: Cardano.BlockSize(response.size),
251-
slotLeader: Cardano.SlotLeader(response.slot_leader),
252-
totalOutput: BigInt(response.output || '0'),
253-
txCount: response.tx_count,
254-
vrf: Cardano.VrfVkBech32(response.block_vrf)
255-
};
256-
});
237+
try {
238+
const responses = await Promise.all(ids.map((id) => this.blockfrost.blocks(id.toString())));
239+
return responses.map((response) => {
240+
if (!response.epoch || !response.epoch_slot || !response.height || !response.slot || !response.block_vrf) {
241+
throw new ProviderError(ProviderFailure.Unknown, null, 'Queried unsupported block');
242+
}
243+
return {
244+
confirmations: response.confirmations,
245+
date: new Date(response.time * 1000),
246+
epoch: Cardano.EpochNo(response.epoch),
247+
epochSlot: response.epoch_slot,
248+
fees: BigInt(response.fees || '0'),
249+
header: {
250+
blockNo: Cardano.BlockNo(response.height),
251+
hash: Cardano.BlockId(response.hash),
252+
slot: Cardano.Slot(response.slot)
253+
},
254+
nextBlock: response.next_block ? Cardano.BlockId(response.next_block) : undefined,
255+
previousBlock: response.previous_block ? Cardano.BlockId(response.previous_block) : undefined,
256+
size: Cardano.BlockSize(response.size),
257+
slotLeader: Cardano.SlotLeader(response.slot_leader),
258+
totalOutput: BigInt(response.output || '0'),
259+
txCount: response.tx_count,
260+
vrf: Cardano.VrfVkBech32(response.block_vrf)
261+
};
262+
});
263+
} catch (error) {
264+
throw blockfrostToProviderError(error);
265+
}
257266
}
258267

259268
public async transactionsByHashes({ ids }: TransactionsByIdsArgs): Promise<Cardano.HydratedTx[]> {
260-
return Promise.all(ids.map((id) => this.fetchTransaction(id)));
269+
try {
270+
return Promise.all(ids.map((id) => this.fetchTransaction(id)));
271+
} catch (error) {
272+
throw blockfrostToProviderError(error);
273+
}
261274
}
262275

263276
public async transactionsByAddresses({
264277
addresses,
265278
blockRange
266279
}: TransactionsByAddressesArgs): Promise<Paginated<Cardano.HydratedTx>> {
267-
const addressTransactions = await Promise.all(
268-
addresses.map(async (address) =>
269-
fetchByAddressSequentially<
270-
{ tx_hash: string; tx_index: number; block_height: number },
271-
BlockfrostTransactionContent
272-
>({
273-
address,
274-
haveEnoughItems: blockRange?.lowerBound
275-
? (transactions) =>
276-
transactions.length > 0 && transactions[transactions.length - 1].block_height < blockRange!.lowerBound!
277-
: undefined,
278-
request: (addr: Cardano.PaymentAddress, paginationOptions) =>
279-
this.blockfrost.addressesTransactions(addr.toString(), paginationOptions)
280-
})
281-
)
282-
);
280+
try {
281+
const addressTransactions = await Promise.all(
282+
addresses.map(async (address) =>
283+
fetchByAddressSequentially<
284+
{ tx_hash: string; tx_index: number; block_height: number },
285+
BlockfrostTransactionContent
286+
>({
287+
address,
288+
haveEnoughItems: blockRange?.lowerBound
289+
? (transactions) =>
290+
transactions.length > 0 &&
291+
transactions[transactions.length - 1].block_height < blockRange!.lowerBound!
292+
: undefined,
293+
request: (addr: Cardano.PaymentAddress, paginationOptions) =>
294+
this.blockfrost.addressesTransactions(addr.toString(), paginationOptions)
295+
})
296+
)
297+
);
283298

284-
const allTransactions = addressTransactions
285-
.flat(1)
286-
.sort((a, b) => b.block_height - a.block_height || b.tx_index - a.tx_index);
287-
const addressTransactionsSinceBlock = blockRange?.lowerBound
288-
? allTransactions.filter(({ block_height }) => block_height >= blockRange!.lowerBound!)
289-
: allTransactions;
290-
const ids = addressTransactionsSinceBlock.map(({ tx_hash }) => Cardano.TransactionId(tx_hash));
291-
const pageResults = await this.transactionsByHashes({ ids });
299+
const allTransactions = addressTransactions
300+
.flat(1)
301+
.sort((a, b) => b.block_height - a.block_height || b.tx_index - a.tx_index);
302+
const addressTransactionsSinceBlock = blockRange?.lowerBound
303+
? allTransactions.filter(({ block_height }) => block_height >= blockRange!.lowerBound!)
304+
: allTransactions;
305+
const ids = addressTransactionsSinceBlock.map(({ tx_hash }) => Cardano.TransactionId(tx_hash));
306+
const pageResults = await this.transactionsByHashes({ ids });
292307

293-
return { pageResults, totalResultCount: allTransactions.length };
308+
return { pageResults, totalResultCount: allTransactions.length };
309+
} catch (error) {
310+
throw blockfrostToProviderError(error);
311+
}
294312
}
295313
}

0 commit comments

Comments
 (0)