Skip to content

Commit cfc27a1

Browse files
committed
feat(cardano-services): implement missing Blockfrost providers features and tests
1 parent 7e797a8 commit cfc27a1

File tree

21 files changed

+1479
-1117
lines changed

21 files changed

+1479
-1117
lines changed
Lines changed: 56 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,85 @@
11
/* eslint-disable unicorn/no-nested-ternary */
2-
import { Asset, AssetProvider, Cardano, ProviderUtil } from '@cardano-sdk/core';
2+
import { Asset, AssetProvider, Cardano, GetAssetArgs, GetAssetsArgs } from '@cardano-sdk/core';
33
import { BlockFrostAPI, Responses } from '@blockfrost/blockfrost-js';
4-
import { blockfrostMetadataToTxMetadata, fetchSequentially, healthCheck, toProviderError } from '../../util';
4+
import { BlockfrostProvider } from '../../util/BlockfrostProvider/BlockfrostProvider';
5+
import { blockfrostMetadataToTxMetadata, blockfrostToProviderError, fetchSequentially } from '../../util';
56
import { replaceNullsWithUndefineds } from '@cardano-sdk/util';
67

7-
const mapMetadata = (
8-
assetId: Cardano.AssetId,
9-
offChain: Responses['asset']['metadata']
10-
): Asset.TokenMetadata | null => {
11-
const { logo, ...metadata } = { ...offChain };
12-
13-
if (Object.values(metadata).every((value) => value === undefined || value === null)) return null;
14-
15-
return {
16-
...replaceNullsWithUndefineds(metadata),
17-
assetId,
18-
desc: metadata.description,
19-
// The other type option is any[] - not sure what it means, omitting if no string.
20-
icon: typeof logo === 'string' ? logo : undefined
21-
};
22-
};
23-
24-
/**
25-
* Connect to the [Blockfrost service](https://docs.blockfrost.io/)
26-
*
27-
* @param {BlockFrostAPI} blockfrost BlockFrostAPI instance
28-
* @returns {AssetProvider} AssetProvider
29-
* @throws ProviderFailure
30-
*/
31-
export const blockfrostAssetProvider = (blockfrost: BlockFrostAPI): AssetProvider => {
32-
const getLastMintedTx = async (assetId: Cardano.AssetId): Promise<Responses['asset_history'][number] | undefined> => {
8+
export class BlockfrostAssetProvider extends BlockfrostProvider implements AssetProvider {
9+
protected async getLastMintedTx(assetId: Cardano.AssetId): Promise<Responses['asset_history'][number] | undefined> {
3310
const [lastMintedTx] = await fetchSequentially({
3411
arg: assetId.toString(),
3512
haveEnoughItems: (items: Responses['asset_history']): boolean => items.length > 0,
3613
paginationOptions: { order: 'desc' },
37-
request: blockfrost.assetsHistory.bind<BlockFrostAPI['assetsHistory']>(blockfrost),
14+
request: this.blockfrost.assetsHistory.bind<BlockFrostAPI['assetsHistory']>(this.blockfrost),
3815
responseTranslator: (response): Responses['asset_history'] => response.filter((tx) => tx.action === 'minted')
3916
});
4017

4118
if (!lastMintedTx) return undefined;
4219
return lastMintedTx;
43-
};
20+
}
4421

45-
const getNftMetadata = async (
22+
protected async getNftMetadata(
4623
asset: Pick<Asset.AssetInfo, 'name' | 'policyId'>,
4724
lastMintedTxHash: string
48-
): Promise<Asset.NftMetadata | null> => {
49-
const metadata = await blockfrost.txsMetadata(lastMintedTxHash);
25+
): Promise<Asset.NftMetadata | null> {
26+
const metadata = await this.blockfrost.txsMetadata(lastMintedTxHash);
5027
// Not sure if types are correct, missing 'label', but it's present in docs
5128
// eslint-disable-next-line @typescript-eslint/no-explicit-any
5229
const metadatumMap = blockfrostMetadataToTxMetadata(metadata as any);
5330
return Asset.NftMetadata.fromMetadatum(asset, metadatumMap, console) ?? null;
54-
};
31+
}
5532

56-
const getAsset: AssetProvider['getAsset'] = async ({ assetId, extraData }) => {
57-
const response = await blockfrost.assetsById(assetId.toString());
58-
const name = Cardano.AssetId.getAssetName(assetId);
59-
const policyId = Cardano.PolicyId(response.policy_id);
60-
const quantity = BigInt(response.quantity);
33+
protected mapMetadata(
34+
assetId: Cardano.AssetId,
35+
offChain: Responses['asset']['metadata']
36+
): Asset.TokenMetadata | null {
37+
const { logo, ...metadata } = { ...offChain };
6138

62-
const nftMetadata = async () => {
63-
let lastMintedTxHash: string = response.initial_mint_tx_hash;
64-
if (response.mint_or_burn_count > 1) {
65-
const lastMintedTx = await getLastMintedTx(assetId);
66-
if (lastMintedTx) lastMintedTxHash = lastMintedTx.tx_hash;
67-
}
68-
return getNftMetadata({ name, policyId }, lastMintedTxHash);
69-
};
39+
if (Object.values(metadata).every((value) => value === undefined || value === null)) return null;
7040

7141
return {
42+
...replaceNullsWithUndefineds(metadata),
7243
assetId,
73-
fingerprint: Cardano.AssetFingerprint(response.fingerprint),
74-
// history: extraData?.history ? await history() : undefined,
75-
mintOrBurnCount: response.mint_or_burn_count,
76-
name,
77-
nftMetadata: extraData?.nftMetadata ? await nftMetadata() : undefined,
78-
policyId,
79-
quantity,
80-
supply: quantity,
81-
tokenMetadata: extraData?.tokenMetadata ? mapMetadata(assetId, response.metadata) : undefined
44+
desc: metadata.description,
45+
// The other type option is any[] - not sure what it means, omitting if no string.
46+
icon: typeof logo === 'string' ? logo : undefined
8247
};
83-
};
48+
}
8449

85-
const getAssets: AssetProvider['getAssets'] = async ({ assetIds, extraData }) =>
86-
Promise.all(assetIds.map((assetId) => getAsset({ assetId, extraData })));
50+
async getAsset({ assetId, extraData }: GetAssetArgs) {
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);
8756

88-
const providerFunctions: AssetProvider = {
89-
getAsset,
90-
getAssets,
91-
healthCheck: healthCheck.bind(undefined, blockfrost)
92-
};
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+
};
9365

94-
return ProviderUtil.withProviderErrors(providerFunctions, toProviderError);
95-
};
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+
}
81+
}
82+
async getAssets({ assetIds, extraData }: GetAssetsArgs) {
83+
return Promise.all(assetIds.map((assetId) => this.getAsset({ assetId, extraData })));
84+
}
85+
}

0 commit comments

Comments
 (0)